virtgpu_submit.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  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. #include <linux/dma-fence-unwrap.h>
  11. #include <linux/file.h>
  12. #include <linux/sync_file.h>
  13. #include <linux/uaccess.h>
  14. #include <drm/drm_file.h>
  15. #include <drm/drm_syncobj.h>
  16. #include <drm/virtgpu_drm.h>
  17. #include "virtgpu_drv.h"
  18. struct virtio_gpu_submit_post_dep {
  19. struct drm_syncobj *syncobj;
  20. struct dma_fence_chain *chain;
  21. u64 point;
  22. };
  23. struct virtio_gpu_submit {
  24. struct virtio_gpu_submit_post_dep *post_deps;
  25. unsigned int num_out_syncobjs;
  26. struct drm_syncobj **in_syncobjs;
  27. unsigned int num_in_syncobjs;
  28. struct virtio_gpu_object_array *buflist;
  29. struct drm_virtgpu_execbuffer *exbuf;
  30. struct virtio_gpu_fence *out_fence;
  31. struct virtio_gpu_fpriv *vfpriv;
  32. struct virtio_gpu_device *vgdev;
  33. struct sync_file *sync_file;
  34. struct drm_file *file;
  35. int out_fence_fd;
  36. u64 fence_ctx;
  37. u32 ring_idx;
  38. void *buf;
  39. };
  40. static int virtio_gpu_do_fence_wait(struct virtio_gpu_submit *submit,
  41. struct dma_fence *in_fence)
  42. {
  43. u64 context = submit->fence_ctx + submit->ring_idx;
  44. if (dma_fence_match_context(in_fence, context))
  45. return 0;
  46. return dma_fence_wait(in_fence, true);
  47. }
  48. static int virtio_gpu_dma_fence_wait(struct virtio_gpu_submit *submit,
  49. struct dma_fence *fence)
  50. {
  51. struct dma_fence_unwrap itr;
  52. struct dma_fence *f;
  53. int err;
  54. dma_fence_unwrap_for_each(f, &itr, fence) {
  55. err = virtio_gpu_do_fence_wait(submit, f);
  56. if (err)
  57. return err;
  58. }
  59. return 0;
  60. }
  61. static void virtio_gpu_free_syncobjs(struct drm_syncobj **syncobjs,
  62. u32 nr_syncobjs)
  63. {
  64. u32 i = nr_syncobjs;
  65. while (i--) {
  66. if (syncobjs[i])
  67. drm_syncobj_put(syncobjs[i]);
  68. }
  69. kvfree(syncobjs);
  70. }
  71. static int
  72. virtio_gpu_parse_deps(struct virtio_gpu_submit *submit)
  73. {
  74. struct drm_virtgpu_execbuffer *exbuf = submit->exbuf;
  75. struct drm_virtgpu_execbuffer_syncobj syncobj_desc;
  76. size_t syncobj_stride = exbuf->syncobj_stride;
  77. u32 num_in_syncobjs = exbuf->num_in_syncobjs;
  78. struct drm_syncobj **syncobjs;
  79. int ret = 0, i;
  80. if (!num_in_syncobjs)
  81. return 0;
  82. /*
  83. * kvmalloc() at first tries to allocate memory using kmalloc() and
  84. * falls back to vmalloc() only on failure. It also uses __GFP_NOWARN
  85. * internally for allocations larger than a page size, preventing
  86. * storm of KMSG warnings.
  87. */
  88. syncobjs = kvzalloc_objs(*syncobjs, num_in_syncobjs);
  89. if (!syncobjs)
  90. return -ENOMEM;
  91. for (i = 0; i < num_in_syncobjs; i++) {
  92. u64 address = exbuf->in_syncobjs + i * syncobj_stride;
  93. struct dma_fence *fence;
  94. memset(&syncobj_desc, 0, sizeof(syncobj_desc));
  95. if (copy_from_user(&syncobj_desc,
  96. u64_to_user_ptr(address),
  97. min(syncobj_stride, sizeof(syncobj_desc)))) {
  98. ret = -EFAULT;
  99. break;
  100. }
  101. if (syncobj_desc.flags & ~VIRTGPU_EXECBUF_SYNCOBJ_FLAGS) {
  102. ret = -EINVAL;
  103. break;
  104. }
  105. ret = drm_syncobj_find_fence(submit->file, syncobj_desc.handle,
  106. syncobj_desc.point, 0, &fence);
  107. if (ret)
  108. break;
  109. ret = virtio_gpu_dma_fence_wait(submit, fence);
  110. dma_fence_put(fence);
  111. if (ret)
  112. break;
  113. if (syncobj_desc.flags & VIRTGPU_EXECBUF_SYNCOBJ_RESET) {
  114. syncobjs[i] = drm_syncobj_find(submit->file,
  115. syncobj_desc.handle);
  116. if (!syncobjs[i]) {
  117. ret = -EINVAL;
  118. break;
  119. }
  120. }
  121. }
  122. if (ret) {
  123. virtio_gpu_free_syncobjs(syncobjs, i);
  124. return ret;
  125. }
  126. submit->num_in_syncobjs = num_in_syncobjs;
  127. submit->in_syncobjs = syncobjs;
  128. return ret;
  129. }
  130. static void virtio_gpu_reset_syncobjs(struct drm_syncobj **syncobjs,
  131. u32 nr_syncobjs)
  132. {
  133. u32 i;
  134. for (i = 0; i < nr_syncobjs; i++) {
  135. if (syncobjs[i])
  136. drm_syncobj_replace_fence(syncobjs[i], NULL);
  137. }
  138. }
  139. static void
  140. virtio_gpu_free_post_deps(struct virtio_gpu_submit_post_dep *post_deps,
  141. u32 nr_syncobjs)
  142. {
  143. u32 i = nr_syncobjs;
  144. while (i--) {
  145. kfree(post_deps[i].chain);
  146. drm_syncobj_put(post_deps[i].syncobj);
  147. }
  148. kvfree(post_deps);
  149. }
  150. static int virtio_gpu_parse_post_deps(struct virtio_gpu_submit *submit)
  151. {
  152. struct drm_virtgpu_execbuffer *exbuf = submit->exbuf;
  153. struct drm_virtgpu_execbuffer_syncobj syncobj_desc;
  154. struct virtio_gpu_submit_post_dep *post_deps;
  155. u32 num_out_syncobjs = exbuf->num_out_syncobjs;
  156. size_t syncobj_stride = exbuf->syncobj_stride;
  157. int ret = 0, i;
  158. if (!num_out_syncobjs)
  159. return 0;
  160. post_deps = kvzalloc_objs(*post_deps, num_out_syncobjs);
  161. if (!post_deps)
  162. return -ENOMEM;
  163. for (i = 0; i < num_out_syncobjs; i++) {
  164. u64 address = exbuf->out_syncobjs + i * syncobj_stride;
  165. memset(&syncobj_desc, 0, sizeof(syncobj_desc));
  166. if (copy_from_user(&syncobj_desc,
  167. u64_to_user_ptr(address),
  168. min(syncobj_stride, sizeof(syncobj_desc)))) {
  169. ret = -EFAULT;
  170. break;
  171. }
  172. post_deps[i].point = syncobj_desc.point;
  173. if (syncobj_desc.flags) {
  174. ret = -EINVAL;
  175. break;
  176. }
  177. if (syncobj_desc.point) {
  178. post_deps[i].chain = dma_fence_chain_alloc();
  179. if (!post_deps[i].chain) {
  180. ret = -ENOMEM;
  181. break;
  182. }
  183. }
  184. post_deps[i].syncobj = drm_syncobj_find(submit->file,
  185. syncobj_desc.handle);
  186. if (!post_deps[i].syncobj) {
  187. kfree(post_deps[i].chain);
  188. ret = -EINVAL;
  189. break;
  190. }
  191. }
  192. if (ret) {
  193. virtio_gpu_free_post_deps(post_deps, i);
  194. return ret;
  195. }
  196. submit->num_out_syncobjs = num_out_syncobjs;
  197. submit->post_deps = post_deps;
  198. return 0;
  199. }
  200. static void
  201. virtio_gpu_process_post_deps(struct virtio_gpu_submit *submit)
  202. {
  203. struct virtio_gpu_submit_post_dep *post_deps = submit->post_deps;
  204. if (post_deps) {
  205. struct dma_fence *fence = &submit->out_fence->f;
  206. u32 i;
  207. for (i = 0; i < submit->num_out_syncobjs; i++) {
  208. if (post_deps[i].chain) {
  209. drm_syncobj_add_point(post_deps[i].syncobj,
  210. post_deps[i].chain,
  211. fence, post_deps[i].point);
  212. post_deps[i].chain = NULL;
  213. } else {
  214. drm_syncobj_replace_fence(post_deps[i].syncobj,
  215. fence);
  216. }
  217. }
  218. }
  219. }
  220. static int virtio_gpu_fence_event_create(struct drm_device *dev,
  221. struct drm_file *file,
  222. struct virtio_gpu_fence *fence,
  223. u32 ring_idx)
  224. {
  225. struct virtio_gpu_fence_event *e = NULL;
  226. int ret;
  227. e = kzalloc_obj(*e);
  228. if (!e)
  229. return -ENOMEM;
  230. e->event.type = VIRTGPU_EVENT_FENCE_SIGNALED;
  231. e->event.length = sizeof(e->event);
  232. ret = drm_event_reserve_init(dev, file, &e->base, &e->event);
  233. if (ret) {
  234. kfree(e);
  235. return ret;
  236. }
  237. fence->e = e;
  238. return 0;
  239. }
  240. static int virtio_gpu_init_submit_buflist(struct virtio_gpu_submit *submit)
  241. {
  242. struct drm_virtgpu_execbuffer *exbuf = submit->exbuf;
  243. u32 *bo_handles;
  244. if (!exbuf->num_bo_handles)
  245. return 0;
  246. bo_handles = kvmalloc_array(exbuf->num_bo_handles, sizeof(*bo_handles),
  247. GFP_KERNEL);
  248. if (!bo_handles)
  249. return -ENOMEM;
  250. if (copy_from_user(bo_handles, u64_to_user_ptr(exbuf->bo_handles),
  251. exbuf->num_bo_handles * sizeof(*bo_handles))) {
  252. kvfree(bo_handles);
  253. return -EFAULT;
  254. }
  255. submit->buflist = virtio_gpu_array_from_handles(submit->file, bo_handles,
  256. exbuf->num_bo_handles);
  257. if (!submit->buflist) {
  258. kvfree(bo_handles);
  259. return -ENOENT;
  260. }
  261. kvfree(bo_handles);
  262. return 0;
  263. }
  264. static void virtio_gpu_cleanup_submit(struct virtio_gpu_submit *submit)
  265. {
  266. virtio_gpu_reset_syncobjs(submit->in_syncobjs, submit->num_in_syncobjs);
  267. virtio_gpu_free_syncobjs(submit->in_syncobjs, submit->num_in_syncobjs);
  268. virtio_gpu_free_post_deps(submit->post_deps, submit->num_out_syncobjs);
  269. if (!IS_ERR(submit->buf))
  270. kvfree(submit->buf);
  271. if (submit->buflist)
  272. virtio_gpu_array_put_free(submit->buflist);
  273. if (submit->out_fence_fd >= 0)
  274. put_unused_fd(submit->out_fence_fd);
  275. if (submit->out_fence)
  276. dma_fence_put(&submit->out_fence->f);
  277. if (submit->sync_file)
  278. fput(submit->sync_file->file);
  279. }
  280. static void virtio_gpu_submit(struct virtio_gpu_submit *submit)
  281. {
  282. virtio_gpu_cmd_submit(submit->vgdev, submit->buf, submit->exbuf->size,
  283. submit->vfpriv->ctx_id, submit->buflist,
  284. submit->out_fence);
  285. virtio_gpu_notify(submit->vgdev);
  286. }
  287. static void virtio_gpu_complete_submit(struct virtio_gpu_submit *submit)
  288. {
  289. submit->buf = NULL;
  290. submit->buflist = NULL;
  291. submit->sync_file = NULL;
  292. submit->out_fence_fd = -1;
  293. }
  294. static int virtio_gpu_init_submit(struct virtio_gpu_submit *submit,
  295. struct drm_virtgpu_execbuffer *exbuf,
  296. struct drm_device *dev,
  297. struct drm_file *file,
  298. u64 fence_ctx, u32 ring_idx)
  299. {
  300. struct virtio_gpu_fpriv *vfpriv = file->driver_priv;
  301. struct virtio_gpu_device *vgdev = dev->dev_private;
  302. struct virtio_gpu_fence *out_fence;
  303. bool drm_fence_event;
  304. int err;
  305. memset(submit, 0, sizeof(*submit));
  306. if ((exbuf->flags & VIRTGPU_EXECBUF_RING_IDX) &&
  307. (vfpriv->ring_idx_mask & BIT_ULL(ring_idx)))
  308. drm_fence_event = true;
  309. else
  310. drm_fence_event = false;
  311. if ((exbuf->flags & VIRTGPU_EXECBUF_FENCE_FD_OUT) ||
  312. exbuf->num_out_syncobjs ||
  313. exbuf->num_bo_handles ||
  314. drm_fence_event)
  315. out_fence = virtio_gpu_fence_alloc(vgdev, fence_ctx, ring_idx);
  316. else
  317. out_fence = NULL;
  318. if (drm_fence_event) {
  319. err = virtio_gpu_fence_event_create(dev, file, out_fence, ring_idx);
  320. if (err) {
  321. dma_fence_put(&out_fence->f);
  322. return err;
  323. }
  324. }
  325. submit->out_fence = out_fence;
  326. submit->fence_ctx = fence_ctx;
  327. submit->ring_idx = ring_idx;
  328. submit->out_fence_fd = -1;
  329. submit->vfpriv = vfpriv;
  330. submit->vgdev = vgdev;
  331. submit->exbuf = exbuf;
  332. submit->file = file;
  333. err = virtio_gpu_init_submit_buflist(submit);
  334. if (err)
  335. return err;
  336. submit->buf = vmemdup_user(u64_to_user_ptr(exbuf->command), exbuf->size);
  337. if (IS_ERR(submit->buf))
  338. return PTR_ERR(submit->buf);
  339. if (exbuf->flags & VIRTGPU_EXECBUF_FENCE_FD_OUT) {
  340. err = get_unused_fd_flags(O_CLOEXEC);
  341. if (err < 0)
  342. return err;
  343. submit->out_fence_fd = err;
  344. submit->sync_file = sync_file_create(&out_fence->f);
  345. if (!submit->sync_file)
  346. return -ENOMEM;
  347. }
  348. return 0;
  349. }
  350. static int virtio_gpu_wait_in_fence(struct virtio_gpu_submit *submit)
  351. {
  352. int ret = 0;
  353. if (submit->exbuf->flags & VIRTGPU_EXECBUF_FENCE_FD_IN) {
  354. struct dma_fence *in_fence =
  355. sync_file_get_fence(submit->exbuf->fence_fd);
  356. if (!in_fence)
  357. return -EINVAL;
  358. /*
  359. * Wait if the fence is from a foreign context, or if the
  360. * fence array contains any fence from a foreign context.
  361. */
  362. ret = virtio_gpu_dma_fence_wait(submit, in_fence);
  363. dma_fence_put(in_fence);
  364. }
  365. return ret;
  366. }
  367. static void virtio_gpu_install_out_fence_fd(struct virtio_gpu_submit *submit)
  368. {
  369. if (submit->sync_file) {
  370. submit->exbuf->fence_fd = submit->out_fence_fd;
  371. fd_install(submit->out_fence_fd, submit->sync_file->file);
  372. }
  373. }
  374. static int virtio_gpu_lock_buflist(struct virtio_gpu_submit *submit)
  375. {
  376. if (submit->buflist)
  377. return virtio_gpu_array_lock_resv(submit->buflist);
  378. return 0;
  379. }
  380. int virtio_gpu_execbuffer_ioctl(struct drm_device *dev, void *data,
  381. struct drm_file *file)
  382. {
  383. struct virtio_gpu_device *vgdev = dev->dev_private;
  384. struct virtio_gpu_fpriv *vfpriv = file->driver_priv;
  385. u64 fence_ctx = vgdev->fence_drv.context;
  386. struct drm_virtgpu_execbuffer *exbuf = data;
  387. struct virtio_gpu_submit submit;
  388. u32 ring_idx = 0;
  389. int ret = -EINVAL;
  390. if (!vgdev->has_virgl_3d)
  391. return -ENOSYS;
  392. if (exbuf->flags & ~VIRTGPU_EXECBUF_FLAGS)
  393. return ret;
  394. if (exbuf->flags & VIRTGPU_EXECBUF_RING_IDX) {
  395. if (exbuf->ring_idx >= vfpriv->num_rings)
  396. return ret;
  397. if (!vfpriv->base_fence_ctx)
  398. return ret;
  399. fence_ctx = vfpriv->base_fence_ctx;
  400. ring_idx = exbuf->ring_idx;
  401. }
  402. virtio_gpu_create_context(dev, file);
  403. ret = virtio_gpu_init_submit(&submit, exbuf, dev, file,
  404. fence_ctx, ring_idx);
  405. if (ret)
  406. goto cleanup;
  407. ret = virtio_gpu_parse_post_deps(&submit);
  408. if (ret)
  409. goto cleanup;
  410. ret = virtio_gpu_parse_deps(&submit);
  411. if (ret)
  412. goto cleanup;
  413. /*
  414. * Await in-fences in the end of the job submission path to
  415. * optimize the path by proceeding directly to the submission
  416. * to virtio after the waits.
  417. */
  418. ret = virtio_gpu_wait_in_fence(&submit);
  419. if (ret)
  420. goto cleanup;
  421. ret = virtio_gpu_lock_buflist(&submit);
  422. if (ret)
  423. goto cleanup;
  424. virtio_gpu_submit(&submit);
  425. /*
  426. * Set up user-out data after submitting the job to optimize
  427. * the job submission path.
  428. */
  429. virtio_gpu_install_out_fence_fd(&submit);
  430. virtio_gpu_process_post_deps(&submit);
  431. virtio_gpu_complete_submit(&submit);
  432. cleanup:
  433. virtio_gpu_cleanup_submit(&submit);
  434. return ret;
  435. }