etnaviv_gem_submit.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597
  1. /*
  2. * Copyright (C) 2015 Etnaviv Project
  3. *
  4. * This program is free software; you can redistribute it and/or modify it
  5. * under the terms of the GNU General Public License version 2 as published by
  6. * the Free Software Foundation.
  7. *
  8. * This program is distributed in the hope that it will be useful, but WITHOUT
  9. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  10. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  11. * more details.
  12. *
  13. * You should have received a copy of the GNU General Public License along with
  14. * this program. If not, see <http://www.gnu.org/licenses/>.
  15. */
  16. #include <linux/dma-fence-array.h>
  17. #include <linux/reservation.h>
  18. #include <linux/sync_file.h>
  19. #include "etnaviv_cmdbuf.h"
  20. #include "etnaviv_drv.h"
  21. #include "etnaviv_gpu.h"
  22. #include "etnaviv_gem.h"
  23. #include "etnaviv_perfmon.h"
  24. #include "etnaviv_sched.h"
  25. /*
  26. * Cmdstream submission:
  27. */
  28. #define BO_INVALID_FLAGS ~(ETNA_SUBMIT_BO_READ | ETNA_SUBMIT_BO_WRITE)
  29. /* make sure these don't conflict w/ ETNAVIV_SUBMIT_BO_x */
  30. #define BO_LOCKED 0x4000
  31. #define BO_PINNED 0x2000
  32. static struct etnaviv_gem_submit *submit_create(struct drm_device *dev,
  33. struct etnaviv_gpu *gpu, size_t nr_bos, size_t nr_pmrs)
  34. {
  35. struct etnaviv_gem_submit *submit;
  36. size_t sz = size_vstruct(nr_bos, sizeof(submit->bos[0]), sizeof(*submit));
  37. submit = kzalloc(sz, GFP_KERNEL);
  38. if (!submit)
  39. return NULL;
  40. submit->pmrs = kcalloc(nr_pmrs, sizeof(struct etnaviv_perfmon_request),
  41. GFP_KERNEL);
  42. if (!submit->pmrs) {
  43. kfree(submit);
  44. return NULL;
  45. }
  46. submit->nr_pmrs = nr_pmrs;
  47. submit->gpu = gpu;
  48. kref_init(&submit->refcount);
  49. return submit;
  50. }
  51. static int submit_lookup_objects(struct etnaviv_gem_submit *submit,
  52. struct drm_file *file, struct drm_etnaviv_gem_submit_bo *submit_bos,
  53. unsigned nr_bos)
  54. {
  55. struct drm_etnaviv_gem_submit_bo *bo;
  56. unsigned i;
  57. int ret = 0;
  58. spin_lock(&file->table_lock);
  59. for (i = 0, bo = submit_bos; i < nr_bos; i++, bo++) {
  60. struct drm_gem_object *obj;
  61. if (bo->flags & BO_INVALID_FLAGS) {
  62. DRM_ERROR("invalid flags: %x\n", bo->flags);
  63. ret = -EINVAL;
  64. goto out_unlock;
  65. }
  66. submit->bos[i].flags = bo->flags;
  67. /* normally use drm_gem_object_lookup(), but for bulk lookup
  68. * all under single table_lock just hit object_idr directly:
  69. */
  70. obj = idr_find(&file->object_idr, bo->handle);
  71. if (!obj) {
  72. DRM_ERROR("invalid handle %u at index %u\n",
  73. bo->handle, i);
  74. ret = -EINVAL;
  75. goto out_unlock;
  76. }
  77. /*
  78. * Take a refcount on the object. The file table lock
  79. * prevents the object_idr's refcount on this being dropped.
  80. */
  81. drm_gem_object_get(obj);
  82. submit->bos[i].obj = to_etnaviv_bo(obj);
  83. }
  84. out_unlock:
  85. submit->nr_bos = i;
  86. spin_unlock(&file->table_lock);
  87. return ret;
  88. }
  89. static void submit_unlock_object(struct etnaviv_gem_submit *submit, int i)
  90. {
  91. if (submit->bos[i].flags & BO_LOCKED) {
  92. struct etnaviv_gem_object *etnaviv_obj = submit->bos[i].obj;
  93. ww_mutex_unlock(&etnaviv_obj->resv->lock);
  94. submit->bos[i].flags &= ~BO_LOCKED;
  95. }
  96. }
  97. static int submit_lock_objects(struct etnaviv_gem_submit *submit,
  98. struct ww_acquire_ctx *ticket)
  99. {
  100. int contended, slow_locked = -1, i, ret = 0;
  101. retry:
  102. for (i = 0; i < submit->nr_bos; i++) {
  103. struct etnaviv_gem_object *etnaviv_obj = submit->bos[i].obj;
  104. if (slow_locked == i)
  105. slow_locked = -1;
  106. contended = i;
  107. if (!(submit->bos[i].flags & BO_LOCKED)) {
  108. ret = ww_mutex_lock_interruptible(&etnaviv_obj->resv->lock,
  109. ticket);
  110. if (ret == -EALREADY)
  111. DRM_ERROR("BO at index %u already on submit list\n",
  112. i);
  113. if (ret)
  114. goto fail;
  115. submit->bos[i].flags |= BO_LOCKED;
  116. }
  117. }
  118. ww_acquire_done(ticket);
  119. return 0;
  120. fail:
  121. for (; i >= 0; i--)
  122. submit_unlock_object(submit, i);
  123. if (slow_locked > 0)
  124. submit_unlock_object(submit, slow_locked);
  125. if (ret == -EDEADLK) {
  126. struct etnaviv_gem_object *etnaviv_obj;
  127. etnaviv_obj = submit->bos[contended].obj;
  128. /* we lost out in a seqno race, lock and retry.. */
  129. ret = ww_mutex_lock_slow_interruptible(&etnaviv_obj->resv->lock,
  130. 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 reservation_object *robj = bo->obj->resv;
  145. if (!(bo->flags & ETNA_SUBMIT_BO_WRITE)) {
  146. ret = reservation_object_reserve_shared(robj);
  147. if (ret)
  148. return ret;
  149. }
  150. if (submit->flags & ETNA_SUBMIT_NO_IMPLICIT)
  151. continue;
  152. if (bo->flags & ETNA_SUBMIT_BO_WRITE) {
  153. ret = reservation_object_get_fences_rcu(robj, &bo->excl,
  154. &bo->nr_shared,
  155. &bo->shared);
  156. if (ret)
  157. return ret;
  158. } else {
  159. bo->excl = reservation_object_get_excl_rcu(robj);
  160. }
  161. }
  162. return ret;
  163. }
  164. static void submit_attach_object_fences(struct etnaviv_gem_submit *submit)
  165. {
  166. int i;
  167. for (i = 0; i < submit->nr_bos; i++) {
  168. struct etnaviv_gem_object *etnaviv_obj = submit->bos[i].obj;
  169. if (submit->bos[i].flags & ETNA_SUBMIT_BO_WRITE)
  170. reservation_object_add_excl_fence(etnaviv_obj->resv,
  171. submit->out_fence);
  172. else
  173. reservation_object_add_shared_fence(etnaviv_obj->resv,
  174. submit->out_fence);
  175. submit_unlock_object(submit, i);
  176. }
  177. }
  178. static int submit_pin_objects(struct etnaviv_gem_submit *submit)
  179. {
  180. int i, ret = 0;
  181. for (i = 0; i < submit->nr_bos; i++) {
  182. struct etnaviv_gem_object *etnaviv_obj = submit->bos[i].obj;
  183. struct etnaviv_vram_mapping *mapping;
  184. mapping = etnaviv_gem_mapping_get(&etnaviv_obj->base,
  185. submit->gpu);
  186. if (IS_ERR(mapping)) {
  187. ret = PTR_ERR(mapping);
  188. break;
  189. }
  190. atomic_inc(&etnaviv_obj->gpu_active);
  191. submit->bos[i].flags |= BO_PINNED;
  192. submit->bos[i].mapping = mapping;
  193. }
  194. return ret;
  195. }
  196. static int submit_bo(struct etnaviv_gem_submit *submit, u32 idx,
  197. struct etnaviv_gem_submit_bo **bo)
  198. {
  199. if (idx >= submit->nr_bos) {
  200. DRM_ERROR("invalid buffer index: %u (out of %u)\n",
  201. idx, submit->nr_bos);
  202. return -EINVAL;
  203. }
  204. *bo = &submit->bos[idx];
  205. return 0;
  206. }
  207. /* process the reloc's and patch up the cmdstream as needed: */
  208. static int submit_reloc(struct etnaviv_gem_submit *submit, void *stream,
  209. u32 size, const struct drm_etnaviv_gem_submit_reloc *relocs,
  210. u32 nr_relocs)
  211. {
  212. u32 i, last_offset = 0;
  213. u32 *ptr = stream;
  214. int ret;
  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->runtime_resumed)
  290. pm_runtime_put_autosuspend(submit->gpu->dev);
  291. if (submit->cmdbuf.suballoc)
  292. etnaviv_cmdbuf_free(&submit->cmdbuf);
  293. for (i = 0; i < submit->nr_bos; i++) {
  294. struct etnaviv_gem_object *etnaviv_obj = submit->bos[i].obj;
  295. /* unpin all objects */
  296. if (submit->bos[i].flags & BO_PINNED) {
  297. etnaviv_gem_mapping_unreference(submit->bos[i].mapping);
  298. atomic_dec(&etnaviv_obj->gpu_active);
  299. submit->bos[i].mapping = NULL;
  300. submit->bos[i].flags &= ~BO_PINNED;
  301. }
  302. /* if the GPU submit failed, objects might still be locked */
  303. submit_unlock_object(submit, i);
  304. drm_gem_object_put_unlocked(&etnaviv_obj->base);
  305. }
  306. wake_up_all(&submit->gpu->fence_event);
  307. if (submit->in_fence)
  308. dma_fence_put(submit->in_fence);
  309. if (submit->out_fence) {
  310. /* first remove from IDR, so fence can not be found anymore */
  311. mutex_lock(&submit->gpu->fence_lock);
  312. idr_remove(&submit->gpu->fence_idr, submit->out_fence_id);
  313. mutex_unlock(&submit->gpu->fence_lock);
  314. dma_fence_put(submit->out_fence);
  315. }
  316. kfree(submit->pmrs);
  317. kfree(submit);
  318. }
  319. void etnaviv_submit_put(struct etnaviv_gem_submit *submit)
  320. {
  321. kref_put(&submit->refcount, submit_cleanup);
  322. }
  323. int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
  324. struct drm_file *file)
  325. {
  326. struct etnaviv_file_private *ctx = file->driver_priv;
  327. struct etnaviv_drm_private *priv = dev->dev_private;
  328. struct drm_etnaviv_gem_submit *args = data;
  329. struct drm_etnaviv_gem_submit_reloc *relocs;
  330. struct drm_etnaviv_gem_submit_pmr *pmrs;
  331. struct drm_etnaviv_gem_submit_bo *bos;
  332. struct etnaviv_gem_submit *submit;
  333. struct etnaviv_gpu *gpu;
  334. struct sync_file *sync_file = NULL;
  335. struct ww_acquire_ctx ticket;
  336. int out_fence_fd = -1;
  337. void *stream;
  338. int ret;
  339. if (args->pipe >= ETNA_MAX_PIPES)
  340. return -EINVAL;
  341. gpu = priv->gpu[args->pipe];
  342. if (!gpu)
  343. return -ENXIO;
  344. if (args->stream_size % 4) {
  345. DRM_ERROR("non-aligned cmdstream buffer size: %u\n",
  346. args->stream_size);
  347. return -EINVAL;
  348. }
  349. if (args->exec_state != ETNA_PIPE_3D &&
  350. args->exec_state != ETNA_PIPE_2D &&
  351. args->exec_state != ETNA_PIPE_VG) {
  352. DRM_ERROR("invalid exec_state: 0x%x\n", args->exec_state);
  353. return -EINVAL;
  354. }
  355. if (args->flags & ~ETNA_SUBMIT_FLAGS) {
  356. DRM_ERROR("invalid flags: 0x%x\n", args->flags);
  357. return -EINVAL;
  358. }
  359. /*
  360. * Copy the command submission and bo array to kernel space in
  361. * one go, and do this outside of any locks.
  362. */
  363. bos = kvmalloc_array(args->nr_bos, sizeof(*bos), GFP_KERNEL);
  364. relocs = kvmalloc_array(args->nr_relocs, sizeof(*relocs), GFP_KERNEL);
  365. pmrs = kvmalloc_array(args->nr_pmrs, sizeof(*pmrs), GFP_KERNEL);
  366. stream = kvmalloc_array(1, args->stream_size, GFP_KERNEL);
  367. if (!bos || !relocs || !pmrs || !stream) {
  368. ret = -ENOMEM;
  369. goto err_submit_cmds;
  370. }
  371. ret = copy_from_user(bos, u64_to_user_ptr(args->bos),
  372. args->nr_bos * sizeof(*bos));
  373. if (ret) {
  374. ret = -EFAULT;
  375. goto err_submit_cmds;
  376. }
  377. ret = copy_from_user(relocs, u64_to_user_ptr(args->relocs),
  378. args->nr_relocs * sizeof(*relocs));
  379. if (ret) {
  380. ret = -EFAULT;
  381. goto err_submit_cmds;
  382. }
  383. ret = copy_from_user(pmrs, u64_to_user_ptr(args->pmrs),
  384. args->nr_pmrs * sizeof(*pmrs));
  385. if (ret) {
  386. ret = -EFAULT;
  387. goto err_submit_cmds;
  388. }
  389. ret = copy_from_user(stream, u64_to_user_ptr(args->stream),
  390. args->stream_size);
  391. if (ret) {
  392. ret = -EFAULT;
  393. goto err_submit_cmds;
  394. }
  395. if (args->flags & ETNA_SUBMIT_FENCE_FD_OUT) {
  396. out_fence_fd = get_unused_fd_flags(O_CLOEXEC);
  397. if (out_fence_fd < 0) {
  398. ret = out_fence_fd;
  399. goto err_submit_cmds;
  400. }
  401. }
  402. ww_acquire_init(&ticket, &reservation_ww_class);
  403. submit = submit_create(dev, gpu, args->nr_bos, args->nr_pmrs);
  404. if (!submit) {
  405. ret = -ENOMEM;
  406. goto err_submit_ww_acquire;
  407. }
  408. ret = etnaviv_cmdbuf_init(gpu->cmdbuf_suballoc, &submit->cmdbuf,
  409. ALIGN(args->stream_size, 8) + 8);
  410. if (ret)
  411. goto err_submit_objects;
  412. submit->cmdbuf.ctx = file->driver_priv;
  413. submit->exec_state = args->exec_state;
  414. submit->flags = args->flags;
  415. ret = submit_lookup_objects(submit, file, bos, args->nr_bos);
  416. if (ret)
  417. goto err_submit_objects;
  418. if (!etnaviv_cmd_validate_one(gpu, stream, args->stream_size / 4,
  419. relocs, args->nr_relocs)) {
  420. ret = -EINVAL;
  421. goto err_submit_objects;
  422. }
  423. if (args->flags & ETNA_SUBMIT_FENCE_FD_IN) {
  424. submit->in_fence = sync_file_get_fence(args->fence_fd);
  425. if (!submit->in_fence) {
  426. ret = -EINVAL;
  427. goto err_submit_objects;
  428. }
  429. }
  430. ret = submit_pin_objects(submit);
  431. if (ret)
  432. goto err_submit_objects;
  433. ret = submit_reloc(submit, stream, args->stream_size / 4,
  434. relocs, args->nr_relocs);
  435. if (ret)
  436. goto err_submit_objects;
  437. ret = submit_perfmon_validate(submit, args->exec_state, pmrs);
  438. if (ret)
  439. goto err_submit_objects;
  440. memcpy(submit->cmdbuf.vaddr, stream, args->stream_size);
  441. ret = submit_lock_objects(submit, &ticket);
  442. if (ret)
  443. goto err_submit_objects;
  444. ret = submit_fence_sync(submit);
  445. if (ret)
  446. goto err_submit_objects;
  447. ret = etnaviv_sched_push_job(&ctx->sched_entity[args->pipe], submit);
  448. if (ret)
  449. goto err_submit_objects;
  450. submit_attach_object_fences(submit);
  451. if (args->flags & ETNA_SUBMIT_FENCE_FD_OUT) {
  452. /*
  453. * This can be improved: ideally we want to allocate the sync
  454. * file before kicking off the GPU job and just attach the
  455. * fence to the sync file here, eliminating the ENOMEM
  456. * possibility at this stage.
  457. */
  458. sync_file = sync_file_create(submit->out_fence);
  459. if (!sync_file) {
  460. ret = -ENOMEM;
  461. goto err_submit_objects;
  462. }
  463. fd_install(out_fence_fd, sync_file->file);
  464. }
  465. args->fence_fd = out_fence_fd;
  466. args->fence = submit->out_fence_id;
  467. err_submit_objects:
  468. etnaviv_submit_put(submit);
  469. err_submit_ww_acquire:
  470. ww_acquire_fini(&ticket);
  471. err_submit_cmds:
  472. if (ret && (out_fence_fd >= 0))
  473. put_unused_fd(out_fence_fd);
  474. if (stream)
  475. kvfree(stream);
  476. if (bos)
  477. kvfree(bos);
  478. if (relocs)
  479. kvfree(relocs);
  480. if (pmrs)
  481. kvfree(pmrs);
  482. return ret;
  483. }