etnaviv_gem_submit.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (C) 2015 Etnaviv Project
  4. */
  5. #include <drm/drm_file.h>
  6. #include <linux/dma-fence-array.h>
  7. #include <linux/file.h>
  8. #include <linux/pm_runtime.h>
  9. #include <linux/dma-resv.h>
  10. #include <linux/sync_file.h>
  11. #include <linux/uaccess.h>
  12. #include <linux/vmalloc.h>
  13. #include "etnaviv_cmdbuf.h"
  14. #include "etnaviv_drv.h"
  15. #include "etnaviv_gpu.h"
  16. #include "etnaviv_gem.h"
  17. #include "etnaviv_perfmon.h"
  18. #include "etnaviv_sched.h"
  19. /*
  20. * Cmdstream submission:
  21. */
  22. #define BO_INVALID_FLAGS ~(ETNA_SUBMIT_BO_READ | ETNA_SUBMIT_BO_WRITE)
  23. /* make sure these don't conflict w/ ETNAVIV_SUBMIT_BO_x */
  24. #define BO_LOCKED 0x4000
  25. #define BO_PINNED 0x2000
  26. static struct etnaviv_gem_submit *submit_create(struct drm_device *dev,
  27. struct etnaviv_gpu *gpu, size_t nr_bos, size_t nr_pmrs)
  28. {
  29. struct etnaviv_gem_submit *submit;
  30. size_t sz = size_vstruct(nr_bos, sizeof(submit->bos[0]), sizeof(*submit));
  31. submit = kzalloc(sz, GFP_KERNEL);
  32. if (!submit)
  33. return NULL;
  34. submit->pmrs = kcalloc(nr_pmrs, sizeof(struct etnaviv_perfmon_request),
  35. GFP_KERNEL);
  36. if (!submit->pmrs) {
  37. kfree(submit);
  38. return NULL;
  39. }
  40. submit->nr_pmrs = nr_pmrs;
  41. submit->gpu = gpu;
  42. kref_init(&submit->refcount);
  43. return submit;
  44. }
  45. static int submit_lookup_objects(struct etnaviv_gem_submit *submit,
  46. struct drm_file *file, struct drm_etnaviv_gem_submit_bo *submit_bos,
  47. unsigned nr_bos)
  48. {
  49. struct drm_etnaviv_gem_submit_bo *bo;
  50. unsigned i;
  51. int ret = 0;
  52. spin_lock(&file->table_lock);
  53. for (i = 0, bo = submit_bos; i < nr_bos; i++, bo++) {
  54. struct drm_gem_object *obj;
  55. if (bo->flags & BO_INVALID_FLAGS) {
  56. DRM_ERROR("invalid flags: %x\n", bo->flags);
  57. ret = -EINVAL;
  58. goto out_unlock;
  59. }
  60. submit->bos[i].flags = bo->flags;
  61. if (submit->flags & ETNA_SUBMIT_SOFTPIN) {
  62. if (bo->presumed < ETNAVIV_SOFTPIN_START_ADDRESS) {
  63. DRM_ERROR("invalid softpin address\n");
  64. ret = -EINVAL;
  65. goto out_unlock;
  66. }
  67. submit->bos[i].va = bo->presumed;
  68. }
  69. /* normally use drm_gem_object_lookup(), but for bulk lookup
  70. * all under single table_lock just hit object_idr directly:
  71. */
  72. obj = idr_find(&file->object_idr, bo->handle);
  73. if (!obj) {
  74. DRM_ERROR("invalid handle %u at index %u\n",
  75. bo->handle, i);
  76. ret = -EINVAL;
  77. goto out_unlock;
  78. }
  79. /*
  80. * Take a refcount on the object. The file table lock
  81. * prevents the object_idr's refcount on this being dropped.
  82. */
  83. drm_gem_object_get(obj);
  84. submit->bos[i].obj = to_etnaviv_bo(obj);
  85. }
  86. out_unlock:
  87. submit->nr_bos = i;
  88. spin_unlock(&file->table_lock);
  89. return ret;
  90. }
  91. static void submit_unlock_object(struct etnaviv_gem_submit *submit, int i)
  92. {
  93. if (submit->bos[i].flags & BO_LOCKED) {
  94. struct drm_gem_object *obj = &submit->bos[i].obj->base;
  95. dma_resv_unlock(obj->resv);
  96. submit->bos[i].flags &= ~BO_LOCKED;
  97. }
  98. }
  99. static int submit_lock_objects(struct etnaviv_gem_submit *submit,
  100. struct ww_acquire_ctx *ticket)
  101. {
  102. int contended, slow_locked = -1, i, ret = 0;
  103. retry:
  104. for (i = 0; i < submit->nr_bos; i++) {
  105. struct drm_gem_object *obj = &submit->bos[i].obj->base;
  106. if (slow_locked == i)
  107. slow_locked = -1;
  108. contended = i;
  109. if (!(submit->bos[i].flags & BO_LOCKED)) {
  110. ret = dma_resv_lock_interruptible(obj->resv, ticket);
  111. if (ret == -EALREADY)
  112. DRM_ERROR("BO at index %u already on submit list\n",
  113. i);
  114. if (ret)
  115. goto fail;
  116. submit->bos[i].flags |= BO_LOCKED;
  117. }
  118. }
  119. ww_acquire_done(ticket);
  120. return 0;
  121. fail:
  122. for (; i >= 0; i--)
  123. submit_unlock_object(submit, i);
  124. if (slow_locked > 0)
  125. submit_unlock_object(submit, slow_locked);
  126. if (ret == -EDEADLK) {
  127. struct drm_gem_object *obj;
  128. obj = &submit->bos[contended].obj->base;
  129. /* we lost out in a seqno race, lock and retry.. */
  130. ret = dma_resv_lock_slow_interruptible(obj->resv, ticket);
  131. if (!ret) {
  132. submit->bos[contended].flags |= BO_LOCKED;
  133. slow_locked = contended;
  134. goto retry;
  135. }
  136. }
  137. return ret;
  138. }
  139. static int submit_fence_sync(struct etnaviv_gem_submit *submit)
  140. {
  141. int i, ret = 0;
  142. for (i = 0; i < submit->nr_bos; i++) {
  143. struct etnaviv_gem_submit_bo *bo = &submit->bos[i];
  144. struct dma_resv *robj = bo->obj->base.resv;
  145. ret = dma_resv_reserve_fences(robj, 1);
  146. if (ret)
  147. return ret;
  148. if (submit->flags & ETNA_SUBMIT_NO_IMPLICIT)
  149. continue;
  150. ret = drm_sched_job_add_implicit_dependencies(&submit->sched_job,
  151. &bo->obj->base,
  152. bo->flags & ETNA_SUBMIT_BO_WRITE);
  153. if (ret)
  154. return ret;
  155. }
  156. return ret;
  157. }
  158. static void submit_attach_object_fences(struct etnaviv_gem_submit *submit)
  159. {
  160. int i;
  161. for (i = 0; i < submit->nr_bos; i++) {
  162. struct drm_gem_object *obj = &submit->bos[i].obj->base;
  163. bool write = submit->bos[i].flags & ETNA_SUBMIT_BO_WRITE;
  164. dma_resv_add_fence(obj->resv, submit->out_fence, write ?
  165. DMA_RESV_USAGE_WRITE : DMA_RESV_USAGE_READ);
  166. submit_unlock_object(submit, i);
  167. }
  168. }
  169. static int submit_pin_objects(struct etnaviv_gem_submit *submit)
  170. {
  171. int i, ret = 0;
  172. for (i = 0; i < submit->nr_bos; i++) {
  173. struct etnaviv_gem_object *etnaviv_obj = submit->bos[i].obj;
  174. struct etnaviv_vram_mapping *mapping;
  175. mapping = etnaviv_gem_mapping_get(&etnaviv_obj->base,
  176. submit->mmu_context,
  177. submit->bos[i].va);
  178. if (IS_ERR(mapping)) {
  179. ret = PTR_ERR(mapping);
  180. break;
  181. }
  182. if ((submit->flags & ETNA_SUBMIT_SOFTPIN) &&
  183. submit->bos[i].va != mapping->iova) {
  184. etnaviv_gem_mapping_unreference(mapping);
  185. return -EINVAL;
  186. }
  187. atomic_inc(&etnaviv_obj->gpu_active);
  188. submit->bos[i].flags |= BO_PINNED;
  189. submit->bos[i].mapping = mapping;
  190. }
  191. return ret;
  192. }
  193. static int submit_bo(struct etnaviv_gem_submit *submit, u32 idx,
  194. struct etnaviv_gem_submit_bo **bo)
  195. {
  196. if (idx >= submit->nr_bos) {
  197. DRM_ERROR("invalid buffer index: %u (out of %u)\n",
  198. idx, submit->nr_bos);
  199. return -EINVAL;
  200. }
  201. *bo = &submit->bos[idx];
  202. return 0;
  203. }
  204. /* process the reloc's and patch up the cmdstream as needed: */
  205. static int submit_reloc(struct etnaviv_gem_submit *submit, void *stream,
  206. u32 size, const struct drm_etnaviv_gem_submit_reloc *relocs,
  207. u32 nr_relocs)
  208. {
  209. u32 i, last_offset = 0;
  210. u32 *ptr = stream;
  211. int ret;
  212. /* Submits using softpin don't blend with relocs */
  213. if ((submit->flags & ETNA_SUBMIT_SOFTPIN) && nr_relocs != 0)
  214. return -EINVAL;
  215. for (i = 0; i < nr_relocs; i++) {
  216. const struct drm_etnaviv_gem_submit_reloc *r = relocs + i;
  217. struct etnaviv_gem_submit_bo *bo;
  218. u32 off;
  219. if (unlikely(r->flags)) {
  220. DRM_ERROR("invalid reloc flags\n");
  221. return -EINVAL;
  222. }
  223. if (r->submit_offset % 4) {
  224. DRM_ERROR("non-aligned reloc offset: %u\n",
  225. r->submit_offset);
  226. return -EINVAL;
  227. }
  228. /* offset in dwords: */
  229. off = r->submit_offset / 4;
  230. if ((off >= size ) ||
  231. (off < last_offset)) {
  232. DRM_ERROR("invalid offset %u at reloc %u\n", off, i);
  233. return -EINVAL;
  234. }
  235. ret = submit_bo(submit, r->reloc_idx, &bo);
  236. if (ret)
  237. return ret;
  238. if (r->reloc_offset > bo->obj->base.size - sizeof(*ptr)) {
  239. DRM_ERROR("relocation %u outside object\n", i);
  240. return -EINVAL;
  241. }
  242. ptr[off] = bo->mapping->iova + r->reloc_offset;
  243. last_offset = off;
  244. }
  245. return 0;
  246. }
  247. static int submit_perfmon_validate(struct etnaviv_gem_submit *submit,
  248. u32 exec_state, const struct drm_etnaviv_gem_submit_pmr *pmrs)
  249. {
  250. u32 i;
  251. for (i = 0; i < submit->nr_pmrs; i++) {
  252. const struct drm_etnaviv_gem_submit_pmr *r = pmrs + i;
  253. struct etnaviv_gem_submit_bo *bo;
  254. int ret;
  255. ret = submit_bo(submit, r->read_idx, &bo);
  256. if (ret)
  257. return ret;
  258. /* at offset 0 a sequence number gets stored used for userspace sync */
  259. if (r->read_offset == 0) {
  260. DRM_ERROR("perfmon request: offset is 0");
  261. return -EINVAL;
  262. }
  263. if (r->read_offset >= bo->obj->base.size - sizeof(u32)) {
  264. DRM_ERROR("perfmon request: offset %u outside object", i);
  265. return -EINVAL;
  266. }
  267. if (r->flags & ~(ETNA_PM_PROCESS_PRE | ETNA_PM_PROCESS_POST)) {
  268. DRM_ERROR("perfmon request: flags are not valid");
  269. return -EINVAL;
  270. }
  271. if (etnaviv_pm_req_validate(r, exec_state)) {
  272. DRM_ERROR("perfmon request: domain or signal not valid");
  273. return -EINVAL;
  274. }
  275. submit->pmrs[i].flags = r->flags;
  276. submit->pmrs[i].domain = r->domain;
  277. submit->pmrs[i].signal = r->signal;
  278. submit->pmrs[i].sequence = r->sequence;
  279. submit->pmrs[i].offset = r->read_offset;
  280. submit->pmrs[i].bo_vma = etnaviv_gem_vmap(&bo->obj->base);
  281. }
  282. return 0;
  283. }
  284. static void submit_cleanup(struct kref *kref)
  285. {
  286. struct etnaviv_gem_submit *submit =
  287. container_of(kref, struct etnaviv_gem_submit, refcount);
  288. unsigned i;
  289. if (submit->cmdbuf.suballoc)
  290. etnaviv_cmdbuf_free(&submit->cmdbuf);
  291. if (submit->mmu_context)
  292. etnaviv_iommu_context_put(submit->mmu_context);
  293. if (submit->prev_mmu_context)
  294. etnaviv_iommu_context_put(submit->prev_mmu_context);
  295. for (i = 0; i < submit->nr_bos; i++) {
  296. struct etnaviv_gem_object *etnaviv_obj = submit->bos[i].obj;
  297. /* unpin all objects */
  298. if (submit->bos[i].flags & BO_PINNED) {
  299. etnaviv_gem_mapping_unreference(submit->bos[i].mapping);
  300. atomic_dec(&etnaviv_obj->gpu_active);
  301. submit->bos[i].mapping = NULL;
  302. submit->bos[i].flags &= ~BO_PINNED;
  303. }
  304. /* if the GPU submit failed, objects might still be locked */
  305. submit_unlock_object(submit, i);
  306. drm_gem_object_put(&etnaviv_obj->base);
  307. }
  308. wake_up_all(&submit->gpu->fence_event);
  309. if (submit->out_fence) {
  310. /*
  311. * Remove from user fence array before dropping the reference,
  312. * so fence can not be found in lookup anymore.
  313. */
  314. xa_erase(&submit->gpu->user_fences, submit->out_fence_id);
  315. dma_fence_put(submit->out_fence);
  316. }
  317. put_pid(submit->pid);
  318. kfree(submit->pmrs);
  319. kfree(submit);
  320. }
  321. void etnaviv_submit_put(struct etnaviv_gem_submit *submit)
  322. {
  323. kref_put(&submit->refcount, submit_cleanup);
  324. }
  325. int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
  326. struct drm_file *file)
  327. {
  328. struct etnaviv_file_private *ctx = file->driver_priv;
  329. struct etnaviv_drm_private *priv = dev->dev_private;
  330. struct drm_etnaviv_gem_submit *args = data;
  331. struct drm_etnaviv_gem_submit_reloc *relocs;
  332. struct drm_etnaviv_gem_submit_pmr *pmrs;
  333. struct drm_etnaviv_gem_submit_bo *bos;
  334. struct etnaviv_gem_submit *submit;
  335. struct etnaviv_gpu *gpu;
  336. struct sync_file *sync_file = NULL;
  337. struct ww_acquire_ctx ticket;
  338. int out_fence_fd = -1;
  339. struct pid *pid = get_pid(task_pid(current));
  340. void *stream;
  341. int ret;
  342. if (args->pipe >= ETNA_MAX_PIPES)
  343. return -EINVAL;
  344. gpu = priv->gpu[args->pipe];
  345. if (!gpu)
  346. return -ENXIO;
  347. if (args->stream_size % 4) {
  348. DRM_ERROR("non-aligned cmdstream buffer size: %u\n",
  349. args->stream_size);
  350. return -EINVAL;
  351. }
  352. if (args->exec_state != ETNA_PIPE_3D &&
  353. args->exec_state != ETNA_PIPE_2D &&
  354. args->exec_state != ETNA_PIPE_VG) {
  355. DRM_ERROR("invalid exec_state: 0x%x\n", args->exec_state);
  356. return -EINVAL;
  357. }
  358. if (args->flags & ~ETNA_SUBMIT_FLAGS) {
  359. DRM_ERROR("invalid flags: 0x%x\n", args->flags);
  360. return -EINVAL;
  361. }
  362. if ((args->flags & ETNA_SUBMIT_SOFTPIN) &&
  363. priv->mmu_global->version != ETNAVIV_IOMMU_V2) {
  364. DRM_ERROR("softpin requested on incompatible MMU\n");
  365. return -EINVAL;
  366. }
  367. if (args->stream_size > SZ_128K || args->nr_relocs > SZ_128K ||
  368. args->nr_bos > SZ_128K || args->nr_pmrs > 128) {
  369. DRM_ERROR("submit arguments out of size limits\n");
  370. return -EINVAL;
  371. }
  372. /*
  373. * Copy the command submission and bo array to kernel space in
  374. * one go, and do this outside of any locks.
  375. */
  376. bos = kvmalloc_array(args->nr_bos, sizeof(*bos), GFP_KERNEL);
  377. relocs = kvmalloc_array(args->nr_relocs, sizeof(*relocs), GFP_KERNEL);
  378. pmrs = kvmalloc_array(args->nr_pmrs, sizeof(*pmrs), GFP_KERNEL);
  379. stream = kvmalloc_array(1, args->stream_size, GFP_KERNEL);
  380. if (!bos || !relocs || !pmrs || !stream) {
  381. ret = -ENOMEM;
  382. goto err_submit_cmds;
  383. }
  384. ret = copy_from_user(bos, u64_to_user_ptr(args->bos),
  385. args->nr_bos * sizeof(*bos));
  386. if (ret) {
  387. ret = -EFAULT;
  388. goto err_submit_cmds;
  389. }
  390. ret = copy_from_user(relocs, u64_to_user_ptr(args->relocs),
  391. args->nr_relocs * sizeof(*relocs));
  392. if (ret) {
  393. ret = -EFAULT;
  394. goto err_submit_cmds;
  395. }
  396. ret = copy_from_user(pmrs, u64_to_user_ptr(args->pmrs),
  397. args->nr_pmrs * sizeof(*pmrs));
  398. if (ret) {
  399. ret = -EFAULT;
  400. goto err_submit_cmds;
  401. }
  402. ret = copy_from_user(stream, u64_to_user_ptr(args->stream),
  403. args->stream_size);
  404. if (ret) {
  405. ret = -EFAULT;
  406. goto err_submit_cmds;
  407. }
  408. if (args->flags & ETNA_SUBMIT_FENCE_FD_OUT) {
  409. out_fence_fd = get_unused_fd_flags(O_CLOEXEC);
  410. if (out_fence_fd < 0) {
  411. ret = out_fence_fd;
  412. goto err_submit_cmds;
  413. }
  414. }
  415. ww_acquire_init(&ticket, &reservation_ww_class);
  416. submit = submit_create(dev, gpu, args->nr_bos, args->nr_pmrs);
  417. if (!submit) {
  418. ret = -ENOMEM;
  419. goto err_submit_ww_acquire;
  420. }
  421. submit->pid = pid;
  422. ret = etnaviv_cmdbuf_init(priv->cmdbuf_suballoc, &submit->cmdbuf,
  423. ALIGN(args->stream_size, 8) + 8);
  424. if (ret)
  425. goto err_submit_put;
  426. submit->ctx = file->driver_priv;
  427. submit->mmu_context = etnaviv_iommu_context_get(submit->ctx->mmu);
  428. submit->exec_state = args->exec_state;
  429. submit->flags = args->flags;
  430. ret = drm_sched_job_init(&submit->sched_job,
  431. &ctx->sched_entity[args->pipe],
  432. 1, submit->ctx);
  433. if (ret)
  434. goto err_submit_put;
  435. ret = submit_lookup_objects(submit, file, bos, args->nr_bos);
  436. if (ret)
  437. goto err_submit_job;
  438. if ((priv->mmu_global->version != ETNAVIV_IOMMU_V2) &&
  439. !etnaviv_cmd_validate_one(gpu, stream, args->stream_size / 4,
  440. relocs, args->nr_relocs)) {
  441. ret = -EINVAL;
  442. goto err_submit_job;
  443. }
  444. if (args->flags & ETNA_SUBMIT_FENCE_FD_IN) {
  445. struct dma_fence *in_fence = sync_file_get_fence(args->fence_fd);
  446. if (!in_fence) {
  447. ret = -EINVAL;
  448. goto err_submit_job;
  449. }
  450. ret = drm_sched_job_add_dependency(&submit->sched_job,
  451. in_fence);
  452. if (ret)
  453. goto err_submit_job;
  454. }
  455. ret = submit_pin_objects(submit);
  456. if (ret)
  457. goto err_submit_job;
  458. ret = submit_reloc(submit, stream, args->stream_size / 4,
  459. relocs, args->nr_relocs);
  460. if (ret)
  461. goto err_submit_job;
  462. ret = submit_perfmon_validate(submit, args->exec_state, pmrs);
  463. if (ret)
  464. goto err_submit_job;
  465. memcpy(submit->cmdbuf.vaddr, stream, args->stream_size);
  466. ret = submit_lock_objects(submit, &ticket);
  467. if (ret)
  468. goto err_submit_job;
  469. ret = submit_fence_sync(submit);
  470. if (ret)
  471. goto err_submit_job;
  472. ret = etnaviv_sched_push_job(submit);
  473. if (ret)
  474. goto err_submit_job;
  475. submit_attach_object_fences(submit);
  476. if (args->flags & ETNA_SUBMIT_FENCE_FD_OUT) {
  477. /*
  478. * This can be improved: ideally we want to allocate the sync
  479. * file before kicking off the GPU job and just attach the
  480. * fence to the sync file here, eliminating the ENOMEM
  481. * possibility at this stage.
  482. */
  483. sync_file = sync_file_create(submit->out_fence);
  484. if (!sync_file) {
  485. ret = -ENOMEM;
  486. /*
  487. * When this late error is hit, the submit has already
  488. * been handed over to the scheduler. At this point
  489. * the sched_job must not be cleaned up.
  490. */
  491. goto err_submit_put;
  492. }
  493. fd_install(out_fence_fd, sync_file->file);
  494. }
  495. args->fence_fd = out_fence_fd;
  496. args->fence = submit->out_fence_id;
  497. err_submit_job:
  498. if (ret)
  499. drm_sched_job_cleanup(&submit->sched_job);
  500. err_submit_put:
  501. etnaviv_submit_put(submit);
  502. err_submit_ww_acquire:
  503. ww_acquire_fini(&ticket);
  504. err_submit_cmds:
  505. if (ret && (out_fence_fd >= 0))
  506. put_unused_fd(out_fence_fd);
  507. kvfree(stream);
  508. kvfree(bos);
  509. kvfree(relocs);
  510. kvfree(pmrs);
  511. return ret;
  512. }