xref: /openbmc/linux/drivers/gpu/drm/virtio/virtgpu_submit.c (revision 0db00e5d86dc793aab9722ad3728d99166eb7d96)
1e4812ab8SDmitry Osipenko // SPDX-License-Identifier: MIT
2e4812ab8SDmitry Osipenko /*
3e4812ab8SDmitry Osipenko  * Copyright (C) 2015 Red Hat, Inc.
4e4812ab8SDmitry Osipenko  * All Rights Reserved.
5e4812ab8SDmitry Osipenko  *
6e4812ab8SDmitry Osipenko  * Authors:
7e4812ab8SDmitry Osipenko  *    Dave Airlie
8e4812ab8SDmitry Osipenko  *    Alon Levy
9e4812ab8SDmitry Osipenko  */
10e4812ab8SDmitry Osipenko 
11e4812ab8SDmitry Osipenko #include <linux/dma-fence-unwrap.h>
12e4812ab8SDmitry Osipenko #include <linux/file.h>
13e4812ab8SDmitry Osipenko #include <linux/sync_file.h>
14e4812ab8SDmitry Osipenko #include <linux/uaccess.h>
15e4812ab8SDmitry Osipenko 
16e4812ab8SDmitry Osipenko #include <drm/drm_file.h>
177cb8d1abSDmitry Osipenko #include <drm/drm_syncobj.h>
18e4812ab8SDmitry Osipenko #include <drm/virtgpu_drm.h>
19e4812ab8SDmitry Osipenko 
20e4812ab8SDmitry Osipenko #include "virtgpu_drv.h"
21e4812ab8SDmitry Osipenko 
227cb8d1abSDmitry Osipenko struct virtio_gpu_submit_post_dep {
237cb8d1abSDmitry Osipenko 	struct drm_syncobj *syncobj;
247cb8d1abSDmitry Osipenko 	struct dma_fence_chain *chain;
257cb8d1abSDmitry Osipenko 	u64 point;
267cb8d1abSDmitry Osipenko };
277cb8d1abSDmitry Osipenko 
28e4812ab8SDmitry Osipenko struct virtio_gpu_submit {
297cb8d1abSDmitry Osipenko 	struct virtio_gpu_submit_post_dep *post_deps;
307cb8d1abSDmitry Osipenko 	unsigned int num_out_syncobjs;
317cb8d1abSDmitry Osipenko 
327cb8d1abSDmitry Osipenko 	struct drm_syncobj **in_syncobjs;
337cb8d1abSDmitry Osipenko 	unsigned int num_in_syncobjs;
347cb8d1abSDmitry Osipenko 
35e4812ab8SDmitry Osipenko 	struct virtio_gpu_object_array *buflist;
36e4812ab8SDmitry Osipenko 	struct drm_virtgpu_execbuffer *exbuf;
37e4812ab8SDmitry Osipenko 	struct virtio_gpu_fence *out_fence;
38e4812ab8SDmitry Osipenko 	struct virtio_gpu_fpriv *vfpriv;
39e4812ab8SDmitry Osipenko 	struct virtio_gpu_device *vgdev;
40e4812ab8SDmitry Osipenko 	struct sync_file *sync_file;
41e4812ab8SDmitry Osipenko 	struct drm_file *file;
42e4812ab8SDmitry Osipenko 	int out_fence_fd;
43e4812ab8SDmitry Osipenko 	u64 fence_ctx;
44e4812ab8SDmitry Osipenko 	u32 ring_idx;
45e4812ab8SDmitry Osipenko 	void *buf;
46e4812ab8SDmitry Osipenko };
47e4812ab8SDmitry Osipenko 
virtio_gpu_do_fence_wait(struct virtio_gpu_submit * submit,struct dma_fence * in_fence)48eba57fb5SDmitry Osipenko static int virtio_gpu_do_fence_wait(struct virtio_gpu_submit *submit,
49e4812ab8SDmitry Osipenko 				    struct dma_fence *in_fence)
50e4812ab8SDmitry Osipenko {
51*a62c9814SDmitry Osipenko 	u64 context = submit->fence_ctx + submit->ring_idx;
52e4812ab8SDmitry Osipenko 
53e4812ab8SDmitry Osipenko 	if (dma_fence_match_context(in_fence, context))
54e4812ab8SDmitry Osipenko 		return 0;
55e4812ab8SDmitry Osipenko 
56e4812ab8SDmitry Osipenko 	return dma_fence_wait(in_fence, true);
57e4812ab8SDmitry Osipenko }
58e4812ab8SDmitry Osipenko 
virtio_gpu_dma_fence_wait(struct virtio_gpu_submit * submit,struct dma_fence * fence)59eba57fb5SDmitry Osipenko static int virtio_gpu_dma_fence_wait(struct virtio_gpu_submit *submit,
60eba57fb5SDmitry Osipenko 				     struct dma_fence *fence)
61eba57fb5SDmitry Osipenko {
62eba57fb5SDmitry Osipenko 	struct dma_fence_unwrap itr;
63eba57fb5SDmitry Osipenko 	struct dma_fence *f;
64eba57fb5SDmitry Osipenko 	int err;
65eba57fb5SDmitry Osipenko 
66eba57fb5SDmitry Osipenko 	dma_fence_unwrap_for_each(f, &itr, fence) {
67eba57fb5SDmitry Osipenko 		err = virtio_gpu_do_fence_wait(submit, f);
68eba57fb5SDmitry Osipenko 		if (err)
69eba57fb5SDmitry Osipenko 			return err;
70eba57fb5SDmitry Osipenko 	}
71eba57fb5SDmitry Osipenko 
72eba57fb5SDmitry Osipenko 	return 0;
73eba57fb5SDmitry Osipenko }
74eba57fb5SDmitry Osipenko 
virtio_gpu_free_syncobjs(struct drm_syncobj ** syncobjs,u32 nr_syncobjs)757cb8d1abSDmitry Osipenko static void virtio_gpu_free_syncobjs(struct drm_syncobj **syncobjs,
767cb8d1abSDmitry Osipenko 				     u32 nr_syncobjs)
777cb8d1abSDmitry Osipenko {
787cb8d1abSDmitry Osipenko 	u32 i = nr_syncobjs;
797cb8d1abSDmitry Osipenko 
807cb8d1abSDmitry Osipenko 	while (i--) {
817cb8d1abSDmitry Osipenko 		if (syncobjs[i])
827cb8d1abSDmitry Osipenko 			drm_syncobj_put(syncobjs[i]);
837cb8d1abSDmitry Osipenko 	}
847cb8d1abSDmitry Osipenko 
857cb8d1abSDmitry Osipenko 	kvfree(syncobjs);
867cb8d1abSDmitry Osipenko }
877cb8d1abSDmitry Osipenko 
887cb8d1abSDmitry Osipenko static int
virtio_gpu_parse_deps(struct virtio_gpu_submit * submit)897cb8d1abSDmitry Osipenko virtio_gpu_parse_deps(struct virtio_gpu_submit *submit)
907cb8d1abSDmitry Osipenko {
917cb8d1abSDmitry Osipenko 	struct drm_virtgpu_execbuffer *exbuf = submit->exbuf;
927cb8d1abSDmitry Osipenko 	struct drm_virtgpu_execbuffer_syncobj syncobj_desc;
937cb8d1abSDmitry Osipenko 	size_t syncobj_stride = exbuf->syncobj_stride;
947cb8d1abSDmitry Osipenko 	u32 num_in_syncobjs = exbuf->num_in_syncobjs;
957cb8d1abSDmitry Osipenko 	struct drm_syncobj **syncobjs;
967cb8d1abSDmitry Osipenko 	int ret = 0, i;
977cb8d1abSDmitry Osipenko 
987cb8d1abSDmitry Osipenko 	if (!num_in_syncobjs)
997cb8d1abSDmitry Osipenko 		return 0;
1007cb8d1abSDmitry Osipenko 
1017cb8d1abSDmitry Osipenko 	/*
1027cb8d1abSDmitry Osipenko 	 * kvalloc at first tries to allocate memory using kmalloc and
1037cb8d1abSDmitry Osipenko 	 * falls back to vmalloc only on failure. It also uses __GFP_NOWARN
1047cb8d1abSDmitry Osipenko 	 * internally for allocations larger than a page size, preventing
1057cb8d1abSDmitry Osipenko 	 * storm of KMSG warnings.
1067cb8d1abSDmitry Osipenko 	 */
1077cb8d1abSDmitry Osipenko 	syncobjs = kvcalloc(num_in_syncobjs, sizeof(*syncobjs), GFP_KERNEL);
1087cb8d1abSDmitry Osipenko 	if (!syncobjs)
1097cb8d1abSDmitry Osipenko 		return -ENOMEM;
1107cb8d1abSDmitry Osipenko 
1117cb8d1abSDmitry Osipenko 	for (i = 0; i < num_in_syncobjs; i++) {
1127cb8d1abSDmitry Osipenko 		u64 address = exbuf->in_syncobjs + i * syncobj_stride;
1137cb8d1abSDmitry Osipenko 		struct dma_fence *fence;
1147cb8d1abSDmitry Osipenko 
1157cb8d1abSDmitry Osipenko 		memset(&syncobj_desc, 0, sizeof(syncobj_desc));
1167cb8d1abSDmitry Osipenko 
1177cb8d1abSDmitry Osipenko 		if (copy_from_user(&syncobj_desc,
1187cb8d1abSDmitry Osipenko 				   u64_to_user_ptr(address),
1197cb8d1abSDmitry Osipenko 				   min(syncobj_stride, sizeof(syncobj_desc)))) {
1207cb8d1abSDmitry Osipenko 			ret = -EFAULT;
1217cb8d1abSDmitry Osipenko 			break;
1227cb8d1abSDmitry Osipenko 		}
1237cb8d1abSDmitry Osipenko 
1247cb8d1abSDmitry Osipenko 		if (syncobj_desc.flags & ~VIRTGPU_EXECBUF_SYNCOBJ_FLAGS) {
1257cb8d1abSDmitry Osipenko 			ret = -EINVAL;
1267cb8d1abSDmitry Osipenko 			break;
1277cb8d1abSDmitry Osipenko 		}
1287cb8d1abSDmitry Osipenko 
1297cb8d1abSDmitry Osipenko 		ret = drm_syncobj_find_fence(submit->file, syncobj_desc.handle,
1307cb8d1abSDmitry Osipenko 					     syncobj_desc.point, 0, &fence);
1317cb8d1abSDmitry Osipenko 		if (ret)
1327cb8d1abSDmitry Osipenko 			break;
1337cb8d1abSDmitry Osipenko 
1347cb8d1abSDmitry Osipenko 		ret = virtio_gpu_dma_fence_wait(submit, fence);
1357cb8d1abSDmitry Osipenko 
1367cb8d1abSDmitry Osipenko 		dma_fence_put(fence);
1377cb8d1abSDmitry Osipenko 		if (ret)
1387cb8d1abSDmitry Osipenko 			break;
1397cb8d1abSDmitry Osipenko 
1407cb8d1abSDmitry Osipenko 		if (syncobj_desc.flags & VIRTGPU_EXECBUF_SYNCOBJ_RESET) {
1417cb8d1abSDmitry Osipenko 			syncobjs[i] = drm_syncobj_find(submit->file,
1427cb8d1abSDmitry Osipenko 						       syncobj_desc.handle);
1437cb8d1abSDmitry Osipenko 			if (!syncobjs[i]) {
1447cb8d1abSDmitry Osipenko 				ret = -EINVAL;
1457cb8d1abSDmitry Osipenko 				break;
1467cb8d1abSDmitry Osipenko 			}
1477cb8d1abSDmitry Osipenko 		}
1487cb8d1abSDmitry Osipenko 	}
1497cb8d1abSDmitry Osipenko 
1507cb8d1abSDmitry Osipenko 	if (ret) {
1517cb8d1abSDmitry Osipenko 		virtio_gpu_free_syncobjs(syncobjs, i);
1527cb8d1abSDmitry Osipenko 		return ret;
1537cb8d1abSDmitry Osipenko 	}
1547cb8d1abSDmitry Osipenko 
1557cb8d1abSDmitry Osipenko 	submit->num_in_syncobjs = num_in_syncobjs;
1567cb8d1abSDmitry Osipenko 	submit->in_syncobjs = syncobjs;
1577cb8d1abSDmitry Osipenko 
1587cb8d1abSDmitry Osipenko 	return ret;
1597cb8d1abSDmitry Osipenko }
1607cb8d1abSDmitry Osipenko 
virtio_gpu_reset_syncobjs(struct drm_syncobj ** syncobjs,u32 nr_syncobjs)1617cb8d1abSDmitry Osipenko static void virtio_gpu_reset_syncobjs(struct drm_syncobj **syncobjs,
1627cb8d1abSDmitry Osipenko 				      u32 nr_syncobjs)
1637cb8d1abSDmitry Osipenko {
1647cb8d1abSDmitry Osipenko 	u32 i;
1657cb8d1abSDmitry Osipenko 
1667cb8d1abSDmitry Osipenko 	for (i = 0; i < nr_syncobjs; i++) {
1677cb8d1abSDmitry Osipenko 		if (syncobjs[i])
1687cb8d1abSDmitry Osipenko 			drm_syncobj_replace_fence(syncobjs[i], NULL);
1697cb8d1abSDmitry Osipenko 	}
1707cb8d1abSDmitry Osipenko }
1717cb8d1abSDmitry Osipenko 
1727cb8d1abSDmitry Osipenko static void
virtio_gpu_free_post_deps(struct virtio_gpu_submit_post_dep * post_deps,u32 nr_syncobjs)1737cb8d1abSDmitry Osipenko virtio_gpu_free_post_deps(struct virtio_gpu_submit_post_dep *post_deps,
1747cb8d1abSDmitry Osipenko 			  u32 nr_syncobjs)
1757cb8d1abSDmitry Osipenko {
1767cb8d1abSDmitry Osipenko 	u32 i = nr_syncobjs;
1777cb8d1abSDmitry Osipenko 
1787cb8d1abSDmitry Osipenko 	while (i--) {
1797cb8d1abSDmitry Osipenko 		kfree(post_deps[i].chain);
1807cb8d1abSDmitry Osipenko 		drm_syncobj_put(post_deps[i].syncobj);
1817cb8d1abSDmitry Osipenko 	}
1827cb8d1abSDmitry Osipenko 
1837cb8d1abSDmitry Osipenko 	kvfree(post_deps);
1847cb8d1abSDmitry Osipenko }
1857cb8d1abSDmitry Osipenko 
virtio_gpu_parse_post_deps(struct virtio_gpu_submit * submit)1867cb8d1abSDmitry Osipenko static int virtio_gpu_parse_post_deps(struct virtio_gpu_submit *submit)
1877cb8d1abSDmitry Osipenko {
1887cb8d1abSDmitry Osipenko 	struct drm_virtgpu_execbuffer *exbuf = submit->exbuf;
1897cb8d1abSDmitry Osipenko 	struct drm_virtgpu_execbuffer_syncobj syncobj_desc;
1907cb8d1abSDmitry Osipenko 	struct virtio_gpu_submit_post_dep *post_deps;
1917cb8d1abSDmitry Osipenko 	u32 num_out_syncobjs = exbuf->num_out_syncobjs;
1927cb8d1abSDmitry Osipenko 	size_t syncobj_stride = exbuf->syncobj_stride;
1937cb8d1abSDmitry Osipenko 	int ret = 0, i;
1947cb8d1abSDmitry Osipenko 
1957cb8d1abSDmitry Osipenko 	if (!num_out_syncobjs)
1967cb8d1abSDmitry Osipenko 		return 0;
1977cb8d1abSDmitry Osipenko 
1987cb8d1abSDmitry Osipenko 	post_deps = kvcalloc(num_out_syncobjs, sizeof(*post_deps), GFP_KERNEL);
1997cb8d1abSDmitry Osipenko 	if (!post_deps)
2007cb8d1abSDmitry Osipenko 		return -ENOMEM;
2017cb8d1abSDmitry Osipenko 
2027cb8d1abSDmitry Osipenko 	for (i = 0; i < num_out_syncobjs; i++) {
2037cb8d1abSDmitry Osipenko 		u64 address = exbuf->out_syncobjs + i * syncobj_stride;
2047cb8d1abSDmitry Osipenko 
2057cb8d1abSDmitry Osipenko 		memset(&syncobj_desc, 0, sizeof(syncobj_desc));
2067cb8d1abSDmitry Osipenko 
2077cb8d1abSDmitry Osipenko 		if (copy_from_user(&syncobj_desc,
2087cb8d1abSDmitry Osipenko 				   u64_to_user_ptr(address),
2097cb8d1abSDmitry Osipenko 				   min(syncobj_stride, sizeof(syncobj_desc)))) {
2107cb8d1abSDmitry Osipenko 			ret = -EFAULT;
2117cb8d1abSDmitry Osipenko 			break;
2127cb8d1abSDmitry Osipenko 		}
2137cb8d1abSDmitry Osipenko 
2147cb8d1abSDmitry Osipenko 		post_deps[i].point = syncobj_desc.point;
2157cb8d1abSDmitry Osipenko 
2167cb8d1abSDmitry Osipenko 		if (syncobj_desc.flags) {
2177cb8d1abSDmitry Osipenko 			ret = -EINVAL;
2187cb8d1abSDmitry Osipenko 			break;
2197cb8d1abSDmitry Osipenko 		}
2207cb8d1abSDmitry Osipenko 
2217cb8d1abSDmitry Osipenko 		if (syncobj_desc.point) {
2227cb8d1abSDmitry Osipenko 			post_deps[i].chain = dma_fence_chain_alloc();
2237cb8d1abSDmitry Osipenko 			if (!post_deps[i].chain) {
2247cb8d1abSDmitry Osipenko 				ret = -ENOMEM;
2257cb8d1abSDmitry Osipenko 				break;
2267cb8d1abSDmitry Osipenko 			}
2277cb8d1abSDmitry Osipenko 		}
2287cb8d1abSDmitry Osipenko 
2297cb8d1abSDmitry Osipenko 		post_deps[i].syncobj = drm_syncobj_find(submit->file,
2307cb8d1abSDmitry Osipenko 							syncobj_desc.handle);
2317cb8d1abSDmitry Osipenko 		if (!post_deps[i].syncobj) {
2327cb8d1abSDmitry Osipenko 			kfree(post_deps[i].chain);
2337cb8d1abSDmitry Osipenko 			ret = -EINVAL;
2347cb8d1abSDmitry Osipenko 			break;
2357cb8d1abSDmitry Osipenko 		}
2367cb8d1abSDmitry Osipenko 	}
2377cb8d1abSDmitry Osipenko 
2387cb8d1abSDmitry Osipenko 	if (ret) {
2397cb8d1abSDmitry Osipenko 		virtio_gpu_free_post_deps(post_deps, i);
2407cb8d1abSDmitry Osipenko 		return ret;
2417cb8d1abSDmitry Osipenko 	}
2427cb8d1abSDmitry Osipenko 
2437cb8d1abSDmitry Osipenko 	submit->num_out_syncobjs = num_out_syncobjs;
2447cb8d1abSDmitry Osipenko 	submit->post_deps = post_deps;
2457cb8d1abSDmitry Osipenko 
2467cb8d1abSDmitry Osipenko 	return 0;
2477cb8d1abSDmitry Osipenko }
2487cb8d1abSDmitry Osipenko 
2497cb8d1abSDmitry Osipenko static void
virtio_gpu_process_post_deps(struct virtio_gpu_submit * submit)2507cb8d1abSDmitry Osipenko virtio_gpu_process_post_deps(struct virtio_gpu_submit *submit)
2517cb8d1abSDmitry Osipenko {
2527cb8d1abSDmitry Osipenko 	struct virtio_gpu_submit_post_dep *post_deps = submit->post_deps;
2537cb8d1abSDmitry Osipenko 
2547cb8d1abSDmitry Osipenko 	if (post_deps) {
2557cb8d1abSDmitry Osipenko 		struct dma_fence *fence = &submit->out_fence->f;
2567cb8d1abSDmitry Osipenko 		u32 i;
2577cb8d1abSDmitry Osipenko 
2587cb8d1abSDmitry Osipenko 		for (i = 0; i < submit->num_out_syncobjs; i++) {
2597cb8d1abSDmitry Osipenko 			if (post_deps[i].chain) {
2607cb8d1abSDmitry Osipenko 				drm_syncobj_add_point(post_deps[i].syncobj,
2617cb8d1abSDmitry Osipenko 						      post_deps[i].chain,
2627cb8d1abSDmitry Osipenko 						      fence, post_deps[i].point);
2637cb8d1abSDmitry Osipenko 				post_deps[i].chain = NULL;
2647cb8d1abSDmitry Osipenko 			} else {
2657cb8d1abSDmitry Osipenko 				drm_syncobj_replace_fence(post_deps[i].syncobj,
2667cb8d1abSDmitry Osipenko 							  fence);
2677cb8d1abSDmitry Osipenko 			}
2687cb8d1abSDmitry Osipenko 		}
2697cb8d1abSDmitry Osipenko 	}
2707cb8d1abSDmitry Osipenko }
2717cb8d1abSDmitry Osipenko 
virtio_gpu_fence_event_create(struct drm_device * dev,struct drm_file * file,struct virtio_gpu_fence * fence,u32 ring_idx)272e4812ab8SDmitry Osipenko static int virtio_gpu_fence_event_create(struct drm_device *dev,
273e4812ab8SDmitry Osipenko 					 struct drm_file *file,
274e4812ab8SDmitry Osipenko 					 struct virtio_gpu_fence *fence,
275e4812ab8SDmitry Osipenko 					 u32 ring_idx)
276e4812ab8SDmitry Osipenko {
277e4812ab8SDmitry Osipenko 	struct virtio_gpu_fence_event *e = NULL;
278e4812ab8SDmitry Osipenko 	int ret;
279e4812ab8SDmitry Osipenko 
280e4812ab8SDmitry Osipenko 	e = kzalloc(sizeof(*e), GFP_KERNEL);
281e4812ab8SDmitry Osipenko 	if (!e)
282e4812ab8SDmitry Osipenko 		return -ENOMEM;
283e4812ab8SDmitry Osipenko 
284e4812ab8SDmitry Osipenko 	e->event.type = VIRTGPU_EVENT_FENCE_SIGNALED;
285e4812ab8SDmitry Osipenko 	e->event.length = sizeof(e->event);
286e4812ab8SDmitry Osipenko 
287e4812ab8SDmitry Osipenko 	ret = drm_event_reserve_init(dev, file, &e->base, &e->event);
288e4812ab8SDmitry Osipenko 	if (ret) {
289e4812ab8SDmitry Osipenko 		kfree(e);
290e4812ab8SDmitry Osipenko 		return ret;
291e4812ab8SDmitry Osipenko 	}
292e4812ab8SDmitry Osipenko 
293e4812ab8SDmitry Osipenko 	fence->e = e;
294e4812ab8SDmitry Osipenko 
295e4812ab8SDmitry Osipenko 	return 0;
296e4812ab8SDmitry Osipenko }
297e4812ab8SDmitry Osipenko 
virtio_gpu_init_submit_buflist(struct virtio_gpu_submit * submit)298e4812ab8SDmitry Osipenko static int virtio_gpu_init_submit_buflist(struct virtio_gpu_submit *submit)
299e4812ab8SDmitry Osipenko {
300e4812ab8SDmitry Osipenko 	struct drm_virtgpu_execbuffer *exbuf = submit->exbuf;
301e4812ab8SDmitry Osipenko 	u32 *bo_handles;
302e4812ab8SDmitry Osipenko 
303e4812ab8SDmitry Osipenko 	if (!exbuf->num_bo_handles)
304e4812ab8SDmitry Osipenko 		return 0;
305e4812ab8SDmitry Osipenko 
306e4812ab8SDmitry Osipenko 	bo_handles = kvmalloc_array(exbuf->num_bo_handles, sizeof(*bo_handles),
307e4812ab8SDmitry Osipenko 				    GFP_KERNEL);
308e4812ab8SDmitry Osipenko 	if (!bo_handles)
309e4812ab8SDmitry Osipenko 		return -ENOMEM;
310e4812ab8SDmitry Osipenko 
311e4812ab8SDmitry Osipenko 	if (copy_from_user(bo_handles, u64_to_user_ptr(exbuf->bo_handles),
312e4812ab8SDmitry Osipenko 			   exbuf->num_bo_handles * sizeof(*bo_handles))) {
313e4812ab8SDmitry Osipenko 		kvfree(bo_handles);
314e4812ab8SDmitry Osipenko 		return -EFAULT;
315e4812ab8SDmitry Osipenko 	}
316e4812ab8SDmitry Osipenko 
317e4812ab8SDmitry Osipenko 	submit->buflist = virtio_gpu_array_from_handles(submit->file, bo_handles,
318e4812ab8SDmitry Osipenko 							exbuf->num_bo_handles);
319e4812ab8SDmitry Osipenko 	if (!submit->buflist) {
320e4812ab8SDmitry Osipenko 		kvfree(bo_handles);
321e4812ab8SDmitry Osipenko 		return -ENOENT;
322e4812ab8SDmitry Osipenko 	}
323e4812ab8SDmitry Osipenko 
324e4812ab8SDmitry Osipenko 	kvfree(bo_handles);
325e4812ab8SDmitry Osipenko 
326e4812ab8SDmitry Osipenko 	return 0;
327e4812ab8SDmitry Osipenko }
328e4812ab8SDmitry Osipenko 
virtio_gpu_cleanup_submit(struct virtio_gpu_submit * submit)329e4812ab8SDmitry Osipenko static void virtio_gpu_cleanup_submit(struct virtio_gpu_submit *submit)
330e4812ab8SDmitry Osipenko {
3317cb8d1abSDmitry Osipenko 	virtio_gpu_reset_syncobjs(submit->in_syncobjs, submit->num_in_syncobjs);
3327cb8d1abSDmitry Osipenko 	virtio_gpu_free_syncobjs(submit->in_syncobjs, submit->num_in_syncobjs);
3337cb8d1abSDmitry Osipenko 	virtio_gpu_free_post_deps(submit->post_deps, submit->num_out_syncobjs);
3347cb8d1abSDmitry Osipenko 
335e4812ab8SDmitry Osipenko 	if (!IS_ERR(submit->buf))
336e4812ab8SDmitry Osipenko 		kvfree(submit->buf);
337e4812ab8SDmitry Osipenko 
338e4812ab8SDmitry Osipenko 	if (submit->buflist)
339e4812ab8SDmitry Osipenko 		virtio_gpu_array_put_free(submit->buflist);
340e4812ab8SDmitry Osipenko 
341e4812ab8SDmitry Osipenko 	if (submit->out_fence_fd >= 0)
342e4812ab8SDmitry Osipenko 		put_unused_fd(submit->out_fence_fd);
343e4812ab8SDmitry Osipenko 
344e4812ab8SDmitry Osipenko 	if (submit->out_fence)
345e4812ab8SDmitry Osipenko 		dma_fence_put(&submit->out_fence->f);
346e4812ab8SDmitry Osipenko 
347e4812ab8SDmitry Osipenko 	if (submit->sync_file)
348e4812ab8SDmitry Osipenko 		fput(submit->sync_file->file);
349e4812ab8SDmitry Osipenko }
350e4812ab8SDmitry Osipenko 
virtio_gpu_submit(struct virtio_gpu_submit * submit)351e4812ab8SDmitry Osipenko static void virtio_gpu_submit(struct virtio_gpu_submit *submit)
352e4812ab8SDmitry Osipenko {
353e4812ab8SDmitry Osipenko 	virtio_gpu_cmd_submit(submit->vgdev, submit->buf, submit->exbuf->size,
354e4812ab8SDmitry Osipenko 			      submit->vfpriv->ctx_id, submit->buflist,
355e4812ab8SDmitry Osipenko 			      submit->out_fence);
356e4812ab8SDmitry Osipenko 	virtio_gpu_notify(submit->vgdev);
357e4812ab8SDmitry Osipenko }
358e4812ab8SDmitry Osipenko 
virtio_gpu_complete_submit(struct virtio_gpu_submit * submit)359e4812ab8SDmitry Osipenko static void virtio_gpu_complete_submit(struct virtio_gpu_submit *submit)
360e4812ab8SDmitry Osipenko {
361e4812ab8SDmitry Osipenko 	submit->buf = NULL;
362e4812ab8SDmitry Osipenko 	submit->buflist = NULL;
363e4812ab8SDmitry Osipenko 	submit->sync_file = NULL;
364e4812ab8SDmitry Osipenko 	submit->out_fence_fd = -1;
365e4812ab8SDmitry Osipenko }
366e4812ab8SDmitry Osipenko 
virtio_gpu_init_submit(struct virtio_gpu_submit * submit,struct drm_virtgpu_execbuffer * exbuf,struct drm_device * dev,struct drm_file * file,u64 fence_ctx,u32 ring_idx)367e4812ab8SDmitry Osipenko static int virtio_gpu_init_submit(struct virtio_gpu_submit *submit,
368e4812ab8SDmitry Osipenko 				  struct drm_virtgpu_execbuffer *exbuf,
369e4812ab8SDmitry Osipenko 				  struct drm_device *dev,
370e4812ab8SDmitry Osipenko 				  struct drm_file *file,
371e4812ab8SDmitry Osipenko 				  u64 fence_ctx, u32 ring_idx)
372e4812ab8SDmitry Osipenko {
373e4812ab8SDmitry Osipenko 	struct virtio_gpu_fpriv *vfpriv = file->driver_priv;
374e4812ab8SDmitry Osipenko 	struct virtio_gpu_device *vgdev = dev->dev_private;
375e4812ab8SDmitry Osipenko 	struct virtio_gpu_fence *out_fence;
37670d1ace5SGurchetan Singh 	bool drm_fence_event;
377e4812ab8SDmitry Osipenko 	int err;
378e4812ab8SDmitry Osipenko 
379e4812ab8SDmitry Osipenko 	memset(submit, 0, sizeof(*submit));
380e4812ab8SDmitry Osipenko 
38170d1ace5SGurchetan Singh 	if ((exbuf->flags & VIRTGPU_EXECBUF_RING_IDX) &&
38270d1ace5SGurchetan Singh 	    (vfpriv->ring_idx_mask & BIT_ULL(ring_idx)))
38370d1ace5SGurchetan Singh 		drm_fence_event = true;
38470d1ace5SGurchetan Singh 	else
38570d1ace5SGurchetan Singh 		drm_fence_event = false;
386e4812ab8SDmitry Osipenko 
38770d1ace5SGurchetan Singh 	if ((exbuf->flags & VIRTGPU_EXECBUF_FENCE_FD_OUT) ||
3887cb8d1abSDmitry Osipenko 	    exbuf->num_out_syncobjs ||
38970d1ace5SGurchetan Singh 	    exbuf->num_bo_handles ||
39070d1ace5SGurchetan Singh 	    drm_fence_event)
39170d1ace5SGurchetan Singh 		out_fence = virtio_gpu_fence_alloc(vgdev, fence_ctx, ring_idx);
39270d1ace5SGurchetan Singh 	else
39370d1ace5SGurchetan Singh 		out_fence = NULL;
39470d1ace5SGurchetan Singh 
39570d1ace5SGurchetan Singh 	if (drm_fence_event) {
396e4812ab8SDmitry Osipenko 		err = virtio_gpu_fence_event_create(dev, file, out_fence, ring_idx);
397e4812ab8SDmitry Osipenko 		if (err) {
398e4812ab8SDmitry Osipenko 			dma_fence_put(&out_fence->f);
399e4812ab8SDmitry Osipenko 			return err;
400e4812ab8SDmitry Osipenko 		}
40170d1ace5SGurchetan Singh 	}
402e4812ab8SDmitry Osipenko 
403e4812ab8SDmitry Osipenko 	submit->out_fence = out_fence;
404e4812ab8SDmitry Osipenko 	submit->fence_ctx = fence_ctx;
405e4812ab8SDmitry Osipenko 	submit->ring_idx = ring_idx;
406e4812ab8SDmitry Osipenko 	submit->out_fence_fd = -1;
407e4812ab8SDmitry Osipenko 	submit->vfpriv = vfpriv;
408e4812ab8SDmitry Osipenko 	submit->vgdev = vgdev;
409e4812ab8SDmitry Osipenko 	submit->exbuf = exbuf;
410e4812ab8SDmitry Osipenko 	submit->file = file;
411e4812ab8SDmitry Osipenko 
412e4812ab8SDmitry Osipenko 	err = virtio_gpu_init_submit_buflist(submit);
413e4812ab8SDmitry Osipenko 	if (err)
414e4812ab8SDmitry Osipenko 		return err;
415e4812ab8SDmitry Osipenko 
416e4812ab8SDmitry Osipenko 	submit->buf = vmemdup_user(u64_to_user_ptr(exbuf->command), exbuf->size);
417e4812ab8SDmitry Osipenko 	if (IS_ERR(submit->buf))
418e4812ab8SDmitry Osipenko 		return PTR_ERR(submit->buf);
419e4812ab8SDmitry Osipenko 
420e4812ab8SDmitry Osipenko 	if (exbuf->flags & VIRTGPU_EXECBUF_FENCE_FD_OUT) {
421e4812ab8SDmitry Osipenko 		err = get_unused_fd_flags(O_CLOEXEC);
422e4812ab8SDmitry Osipenko 		if (err < 0)
423e4812ab8SDmitry Osipenko 			return err;
424e4812ab8SDmitry Osipenko 
425e4812ab8SDmitry Osipenko 		submit->out_fence_fd = err;
426e4812ab8SDmitry Osipenko 
427e4812ab8SDmitry Osipenko 		submit->sync_file = sync_file_create(&out_fence->f);
428e4812ab8SDmitry Osipenko 		if (!submit->sync_file)
429e4812ab8SDmitry Osipenko 			return -ENOMEM;
430e4812ab8SDmitry Osipenko 	}
431e4812ab8SDmitry Osipenko 
432e4812ab8SDmitry Osipenko 	return 0;
433e4812ab8SDmitry Osipenko }
434e4812ab8SDmitry Osipenko 
virtio_gpu_wait_in_fence(struct virtio_gpu_submit * submit)435e4812ab8SDmitry Osipenko static int virtio_gpu_wait_in_fence(struct virtio_gpu_submit *submit)
436e4812ab8SDmitry Osipenko {
437e4812ab8SDmitry Osipenko 	int ret = 0;
438e4812ab8SDmitry Osipenko 
439e4812ab8SDmitry Osipenko 	if (submit->exbuf->flags & VIRTGPU_EXECBUF_FENCE_FD_IN) {
440e4812ab8SDmitry Osipenko 		struct dma_fence *in_fence =
441e4812ab8SDmitry Osipenko 				sync_file_get_fence(submit->exbuf->fence_fd);
442e4812ab8SDmitry Osipenko 		if (!in_fence)
443e4812ab8SDmitry Osipenko 			return -EINVAL;
444e4812ab8SDmitry Osipenko 
445e4812ab8SDmitry Osipenko 		/*
446e4812ab8SDmitry Osipenko 		 * Wait if the fence is from a foreign context, or if the
447e4812ab8SDmitry Osipenko 		 * fence array contains any fence from a foreign context.
448e4812ab8SDmitry Osipenko 		 */
449e4812ab8SDmitry Osipenko 		ret = virtio_gpu_dma_fence_wait(submit, in_fence);
450e4812ab8SDmitry Osipenko 
451e4812ab8SDmitry Osipenko 		dma_fence_put(in_fence);
452e4812ab8SDmitry Osipenko 	}
453e4812ab8SDmitry Osipenko 
454e4812ab8SDmitry Osipenko 	return ret;
455e4812ab8SDmitry Osipenko }
456e4812ab8SDmitry Osipenko 
virtio_gpu_install_out_fence_fd(struct virtio_gpu_submit * submit)457e4812ab8SDmitry Osipenko static void virtio_gpu_install_out_fence_fd(struct virtio_gpu_submit *submit)
458e4812ab8SDmitry Osipenko {
459e4812ab8SDmitry Osipenko 	if (submit->sync_file) {
460e4812ab8SDmitry Osipenko 		submit->exbuf->fence_fd = submit->out_fence_fd;
461e4812ab8SDmitry Osipenko 		fd_install(submit->out_fence_fd, submit->sync_file->file);
462e4812ab8SDmitry Osipenko 	}
463e4812ab8SDmitry Osipenko }
464e4812ab8SDmitry Osipenko 
virtio_gpu_lock_buflist(struct virtio_gpu_submit * submit)465e4812ab8SDmitry Osipenko static int virtio_gpu_lock_buflist(struct virtio_gpu_submit *submit)
466e4812ab8SDmitry Osipenko {
467e4812ab8SDmitry Osipenko 	if (submit->buflist)
468e4812ab8SDmitry Osipenko 		return virtio_gpu_array_lock_resv(submit->buflist);
469e4812ab8SDmitry Osipenko 
470e4812ab8SDmitry Osipenko 	return 0;
471e4812ab8SDmitry Osipenko }
472e4812ab8SDmitry Osipenko 
virtio_gpu_execbuffer_ioctl(struct drm_device * dev,void * data,struct drm_file * file)473e4812ab8SDmitry Osipenko int virtio_gpu_execbuffer_ioctl(struct drm_device *dev, void *data,
474e4812ab8SDmitry Osipenko 				struct drm_file *file)
475e4812ab8SDmitry Osipenko {
476e4812ab8SDmitry Osipenko 	struct virtio_gpu_device *vgdev = dev->dev_private;
477e4812ab8SDmitry Osipenko 	struct virtio_gpu_fpriv *vfpriv = file->driver_priv;
478e4812ab8SDmitry Osipenko 	u64 fence_ctx = vgdev->fence_drv.context;
479e4812ab8SDmitry Osipenko 	struct drm_virtgpu_execbuffer *exbuf = data;
480e4812ab8SDmitry Osipenko 	struct virtio_gpu_submit submit;
481e4812ab8SDmitry Osipenko 	u32 ring_idx = 0;
482e4812ab8SDmitry Osipenko 	int ret = -EINVAL;
483e4812ab8SDmitry Osipenko 
484e4812ab8SDmitry Osipenko 	if (!vgdev->has_virgl_3d)
485e4812ab8SDmitry Osipenko 		return -ENOSYS;
486e4812ab8SDmitry Osipenko 
487e4812ab8SDmitry Osipenko 	if (exbuf->flags & ~VIRTGPU_EXECBUF_FLAGS)
488e4812ab8SDmitry Osipenko 		return ret;
489e4812ab8SDmitry Osipenko 
490e4812ab8SDmitry Osipenko 	if (exbuf->flags & VIRTGPU_EXECBUF_RING_IDX) {
491e4812ab8SDmitry Osipenko 		if (exbuf->ring_idx >= vfpriv->num_rings)
492e4812ab8SDmitry Osipenko 			return ret;
493e4812ab8SDmitry Osipenko 
494e4812ab8SDmitry Osipenko 		if (!vfpriv->base_fence_ctx)
495e4812ab8SDmitry Osipenko 			return ret;
496e4812ab8SDmitry Osipenko 
497e4812ab8SDmitry Osipenko 		fence_ctx = vfpriv->base_fence_ctx;
498e4812ab8SDmitry Osipenko 		ring_idx = exbuf->ring_idx;
499e4812ab8SDmitry Osipenko 	}
500e4812ab8SDmitry Osipenko 
501e4812ab8SDmitry Osipenko 	virtio_gpu_create_context(dev, file);
502e4812ab8SDmitry Osipenko 
503e4812ab8SDmitry Osipenko 	ret = virtio_gpu_init_submit(&submit, exbuf, dev, file,
504e4812ab8SDmitry Osipenko 				     fence_ctx, ring_idx);
505e4812ab8SDmitry Osipenko 	if (ret)
506e4812ab8SDmitry Osipenko 		goto cleanup;
507e4812ab8SDmitry Osipenko 
5087cb8d1abSDmitry Osipenko 	ret = virtio_gpu_parse_post_deps(&submit);
5097cb8d1abSDmitry Osipenko 	if (ret)
5107cb8d1abSDmitry Osipenko 		goto cleanup;
5117cb8d1abSDmitry Osipenko 
5127cb8d1abSDmitry Osipenko 	ret = virtio_gpu_parse_deps(&submit);
5137cb8d1abSDmitry Osipenko 	if (ret)
5147cb8d1abSDmitry Osipenko 		goto cleanup;
5157cb8d1abSDmitry Osipenko 
516e4812ab8SDmitry Osipenko 	/*
517e4812ab8SDmitry Osipenko 	 * Await in-fences in the end of the job submission path to
518e4812ab8SDmitry Osipenko 	 * optimize the path by proceeding directly to the submission
519e4812ab8SDmitry Osipenko 	 * to virtio after the waits.
520e4812ab8SDmitry Osipenko 	 */
521e4812ab8SDmitry Osipenko 	ret = virtio_gpu_wait_in_fence(&submit);
522e4812ab8SDmitry Osipenko 	if (ret)
523e4812ab8SDmitry Osipenko 		goto cleanup;
524e4812ab8SDmitry Osipenko 
525e4812ab8SDmitry Osipenko 	ret = virtio_gpu_lock_buflist(&submit);
526e4812ab8SDmitry Osipenko 	if (ret)
527e4812ab8SDmitry Osipenko 		goto cleanup;
528e4812ab8SDmitry Osipenko 
529e4812ab8SDmitry Osipenko 	virtio_gpu_submit(&submit);
530e4812ab8SDmitry Osipenko 
531e4812ab8SDmitry Osipenko 	/*
532e4812ab8SDmitry Osipenko 	 * Set up usr-out data after submitting the job to optimize
533e4812ab8SDmitry Osipenko 	 * the job submission path.
534e4812ab8SDmitry Osipenko 	 */
535e4812ab8SDmitry Osipenko 	virtio_gpu_install_out_fence_fd(&submit);
5367cb8d1abSDmitry Osipenko 	virtio_gpu_process_post_deps(&submit);
537e4812ab8SDmitry Osipenko 	virtio_gpu_complete_submit(&submit);
538e4812ab8SDmitry Osipenko cleanup:
539e4812ab8SDmitry Osipenko 	virtio_gpu_cleanup_submit(&submit);
540e4812ab8SDmitry Osipenko 
541e4812ab8SDmitry Osipenko 	return ret;
542e4812ab8SDmitry Osipenko }
543