1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (C) 2015 Etnaviv Project
6 #include <drm/drm_file.h>
7 #include <linux/dma-fence-array.h>
8 #include <linux/file.h>
9 #include <linux/pm_runtime.h>
10 #include <linux/dma-resv.h>
11 #include <linux/sync_file.h>
12 #include <linux/uaccess.h>
13 #include <linux/vmalloc.h>
15 #include "etnaviv_cmdbuf.h"
16 #include "etnaviv_drv.h"
17 #include "etnaviv_gpu.h"
18 #include "etnaviv_gem.h"
19 #include "etnaviv_perfmon.h"
20 #include "etnaviv_sched.h"
23 * Cmdstream submission:
26 #define BO_INVALID_FLAGS ~(ETNA_SUBMIT_BO_READ | ETNA_SUBMIT_BO_WRITE)
27 /* make sure these don't conflict w/ ETNAVIV_SUBMIT_BO_x */
28 #define BO_LOCKED 0x4000
29 #define BO_PINNED 0x2000
31 static struct etnaviv_gem_submit
*submit_create(struct drm_device
*dev
,
32 struct etnaviv_gpu
*gpu
, size_t nr_bos
, size_t nr_pmrs
)
34 struct etnaviv_gem_submit
*submit
;
35 size_t sz
= size_vstruct(nr_bos
, sizeof(submit
->bos
[0]), sizeof(*submit
));
37 submit
= kzalloc(sz
, GFP_KERNEL
);
41 submit
->pmrs
= kcalloc(nr_pmrs
, sizeof(struct etnaviv_perfmon_request
),
47 submit
->nr_pmrs
= nr_pmrs
;
50 kref_init(&submit
->refcount
);
55 static int submit_lookup_objects(struct etnaviv_gem_submit
*submit
,
56 struct drm_file
*file
, struct drm_etnaviv_gem_submit_bo
*submit_bos
,
59 struct drm_etnaviv_gem_submit_bo
*bo
;
63 spin_lock(&file
->table_lock
);
65 for (i
= 0, bo
= submit_bos
; i
< nr_bos
; i
++, bo
++) {
66 struct drm_gem_object
*obj
;
68 if (bo
->flags
& BO_INVALID_FLAGS
) {
69 DRM_ERROR("invalid flags: %x\n", bo
->flags
);
74 submit
->bos
[i
].flags
= bo
->flags
;
75 if (submit
->flags
& ETNA_SUBMIT_SOFTPIN
) {
76 if (bo
->presumed
< ETNAVIV_SOFTPIN_START_ADDRESS
) {
77 DRM_ERROR("invalid softpin address\n");
81 submit
->bos
[i
].va
= bo
->presumed
;
84 /* normally use drm_gem_object_lookup(), but for bulk lookup
85 * all under single table_lock just hit object_idr directly:
87 obj
= idr_find(&file
->object_idr
, bo
->handle
);
89 DRM_ERROR("invalid handle %u at index %u\n",
96 * Take a refcount on the object. The file table lock
97 * prevents the object_idr's refcount on this being dropped.
99 drm_gem_object_get(obj
);
101 submit
->bos
[i
].obj
= to_etnaviv_bo(obj
);
106 spin_unlock(&file
->table_lock
);
111 static void submit_unlock_object(struct etnaviv_gem_submit
*submit
, int i
)
113 if (submit
->bos
[i
].flags
& BO_LOCKED
) {
114 struct drm_gem_object
*obj
= &submit
->bos
[i
].obj
->base
;
116 dma_resv_unlock(obj
->resv
);
117 submit
->bos
[i
].flags
&= ~BO_LOCKED
;
121 static int submit_lock_objects(struct etnaviv_gem_submit
*submit
,
122 struct ww_acquire_ctx
*ticket
)
124 int contended
, slow_locked
= -1, i
, ret
= 0;
127 for (i
= 0; i
< submit
->nr_bos
; i
++) {
128 struct drm_gem_object
*obj
= &submit
->bos
[i
].obj
->base
;
130 if (slow_locked
== i
)
135 if (!(submit
->bos
[i
].flags
& BO_LOCKED
)) {
136 ret
= dma_resv_lock_interruptible(obj
->resv
, ticket
);
137 if (ret
== -EALREADY
)
138 DRM_ERROR("BO at index %u already on submit list\n",
142 submit
->bos
[i
].flags
|= BO_LOCKED
;
146 ww_acquire_done(ticket
);
152 submit_unlock_object(submit
, i
);
155 submit_unlock_object(submit
, slow_locked
);
157 if (ret
== -EDEADLK
) {
158 struct drm_gem_object
*obj
;
160 obj
= &submit
->bos
[contended
].obj
->base
;
162 /* we lost out in a seqno race, lock and retry.. */
163 ret
= dma_resv_lock_slow_interruptible(obj
->resv
, ticket
);
165 submit
->bos
[contended
].flags
|= BO_LOCKED
;
166 slow_locked
= contended
;
174 static int submit_fence_sync(struct etnaviv_gem_submit
*submit
)
178 for (i
= 0; i
< submit
->nr_bos
; i
++) {
179 struct etnaviv_gem_submit_bo
*bo
= &submit
->bos
[i
];
180 struct dma_resv
*robj
= bo
->obj
->base
.resv
;
182 if (!(bo
->flags
& ETNA_SUBMIT_BO_WRITE
)) {
183 ret
= dma_resv_reserve_shared(robj
, 1);
188 if (submit
->flags
& ETNA_SUBMIT_NO_IMPLICIT
)
191 if (bo
->flags
& ETNA_SUBMIT_BO_WRITE
) {
192 ret
= dma_resv_get_fences_rcu(robj
, &bo
->excl
,
198 bo
->excl
= dma_resv_get_excl_rcu(robj
);
206 static void submit_attach_object_fences(struct etnaviv_gem_submit
*submit
)
210 for (i
= 0; i
< submit
->nr_bos
; i
++) {
211 struct drm_gem_object
*obj
= &submit
->bos
[i
].obj
->base
;
213 if (submit
->bos
[i
].flags
& ETNA_SUBMIT_BO_WRITE
)
214 dma_resv_add_excl_fence(obj
->resv
,
217 dma_resv_add_shared_fence(obj
->resv
,
220 submit_unlock_object(submit
, i
);
224 static int submit_pin_objects(struct etnaviv_gem_submit
*submit
)
228 for (i
= 0; i
< submit
->nr_bos
; i
++) {
229 struct etnaviv_gem_object
*etnaviv_obj
= submit
->bos
[i
].obj
;
230 struct etnaviv_vram_mapping
*mapping
;
232 mapping
= etnaviv_gem_mapping_get(&etnaviv_obj
->base
,
235 if (IS_ERR(mapping
)) {
236 ret
= PTR_ERR(mapping
);
240 if ((submit
->flags
& ETNA_SUBMIT_SOFTPIN
) &&
241 submit
->bos
[i
].va
!= mapping
->iova
)
244 atomic_inc(&etnaviv_obj
->gpu_active
);
246 submit
->bos
[i
].flags
|= BO_PINNED
;
247 submit
->bos
[i
].mapping
= mapping
;
253 static int submit_bo(struct etnaviv_gem_submit
*submit
, u32 idx
,
254 struct etnaviv_gem_submit_bo
**bo
)
256 if (idx
>= submit
->nr_bos
) {
257 DRM_ERROR("invalid buffer index: %u (out of %u)\n",
258 idx
, submit
->nr_bos
);
262 *bo
= &submit
->bos
[idx
];
267 /* process the reloc's and patch up the cmdstream as needed: */
268 static int submit_reloc(struct etnaviv_gem_submit
*submit
, void *stream
,
269 u32 size
, const struct drm_etnaviv_gem_submit_reloc
*relocs
,
272 u32 i
, last_offset
= 0;
276 /* Submits using softpin don't blend with relocs */
277 if ((submit
->flags
& ETNA_SUBMIT_SOFTPIN
) && nr_relocs
!= 0)
280 for (i
= 0; i
< nr_relocs
; i
++) {
281 const struct drm_etnaviv_gem_submit_reloc
*r
= relocs
+ i
;
282 struct etnaviv_gem_submit_bo
*bo
;
285 if (unlikely(r
->flags
)) {
286 DRM_ERROR("invalid reloc flags\n");
290 if (r
->submit_offset
% 4) {
291 DRM_ERROR("non-aligned reloc offset: %u\n",
296 /* offset in dwords: */
297 off
= r
->submit_offset
/ 4;
299 if ((off
>= size
) ||
300 (off
< last_offset
)) {
301 DRM_ERROR("invalid offset %u at reloc %u\n", off
, i
);
305 ret
= submit_bo(submit
, r
->reloc_idx
, &bo
);
309 if (r
->reloc_offset
> bo
->obj
->base
.size
- sizeof(*ptr
)) {
310 DRM_ERROR("relocation %u outside object\n", i
);
314 ptr
[off
] = bo
->mapping
->iova
+ r
->reloc_offset
;
322 static int submit_perfmon_validate(struct etnaviv_gem_submit
*submit
,
323 u32 exec_state
, const struct drm_etnaviv_gem_submit_pmr
*pmrs
)
327 for (i
= 0; i
< submit
->nr_pmrs
; i
++) {
328 const struct drm_etnaviv_gem_submit_pmr
*r
= pmrs
+ i
;
329 struct etnaviv_gem_submit_bo
*bo
;
332 ret
= submit_bo(submit
, r
->read_idx
, &bo
);
336 /* at offset 0 a sequence number gets stored used for userspace sync */
337 if (r
->read_offset
== 0) {
338 DRM_ERROR("perfmon request: offset is 0");
342 if (r
->read_offset
>= bo
->obj
->base
.size
- sizeof(u32
)) {
343 DRM_ERROR("perfmon request: offset %u outside object", i
);
347 if (r
->flags
& ~(ETNA_PM_PROCESS_PRE
| ETNA_PM_PROCESS_POST
)) {
348 DRM_ERROR("perfmon request: flags are not valid");
352 if (etnaviv_pm_req_validate(r
, exec_state
)) {
353 DRM_ERROR("perfmon request: domain or signal not valid");
357 submit
->pmrs
[i
].flags
= r
->flags
;
358 submit
->pmrs
[i
].domain
= r
->domain
;
359 submit
->pmrs
[i
].signal
= r
->signal
;
360 submit
->pmrs
[i
].sequence
= r
->sequence
;
361 submit
->pmrs
[i
].offset
= r
->read_offset
;
362 submit
->pmrs
[i
].bo_vma
= etnaviv_gem_vmap(&bo
->obj
->base
);
368 static void submit_cleanup(struct kref
*kref
)
370 struct etnaviv_gem_submit
*submit
=
371 container_of(kref
, struct etnaviv_gem_submit
, refcount
);
374 if (submit
->runtime_resumed
)
375 pm_runtime_put_autosuspend(submit
->gpu
->dev
);
377 if (submit
->cmdbuf
.suballoc
)
378 etnaviv_cmdbuf_free(&submit
->cmdbuf
);
380 if (submit
->mmu_context
)
381 etnaviv_iommu_context_put(submit
->mmu_context
);
383 if (submit
->prev_mmu_context
)
384 etnaviv_iommu_context_put(submit
->prev_mmu_context
);
386 for (i
= 0; i
< submit
->nr_bos
; i
++) {
387 struct etnaviv_gem_object
*etnaviv_obj
= submit
->bos
[i
].obj
;
389 /* unpin all objects */
390 if (submit
->bos
[i
].flags
& BO_PINNED
) {
391 etnaviv_gem_mapping_unreference(submit
->bos
[i
].mapping
);
392 atomic_dec(&etnaviv_obj
->gpu_active
);
393 submit
->bos
[i
].mapping
= NULL
;
394 submit
->bos
[i
].flags
&= ~BO_PINNED
;
397 /* if the GPU submit failed, objects might still be locked */
398 submit_unlock_object(submit
, i
);
399 drm_gem_object_put_unlocked(&etnaviv_obj
->base
);
402 wake_up_all(&submit
->gpu
->fence_event
);
404 if (submit
->in_fence
)
405 dma_fence_put(submit
->in_fence
);
406 if (submit
->out_fence
) {
407 /* first remove from IDR, so fence can not be found anymore */
408 mutex_lock(&submit
->gpu
->fence_lock
);
409 idr_remove(&submit
->gpu
->fence_idr
, submit
->out_fence_id
);
410 mutex_unlock(&submit
->gpu
->fence_lock
);
411 dma_fence_put(submit
->out_fence
);
417 void etnaviv_submit_put(struct etnaviv_gem_submit
*submit
)
419 kref_put(&submit
->refcount
, submit_cleanup
);
422 int etnaviv_ioctl_gem_submit(struct drm_device
*dev
, void *data
,
423 struct drm_file
*file
)
425 struct etnaviv_file_private
*ctx
= file
->driver_priv
;
426 struct etnaviv_drm_private
*priv
= dev
->dev_private
;
427 struct drm_etnaviv_gem_submit
*args
= data
;
428 struct drm_etnaviv_gem_submit_reloc
*relocs
;
429 struct drm_etnaviv_gem_submit_pmr
*pmrs
;
430 struct drm_etnaviv_gem_submit_bo
*bos
;
431 struct etnaviv_gem_submit
*submit
;
432 struct etnaviv_gpu
*gpu
;
433 struct sync_file
*sync_file
= NULL
;
434 struct ww_acquire_ctx ticket
;
435 int out_fence_fd
= -1;
439 if (args
->pipe
>= ETNA_MAX_PIPES
)
442 gpu
= priv
->gpu
[args
->pipe
];
446 if (args
->stream_size
% 4) {
447 DRM_ERROR("non-aligned cmdstream buffer size: %u\n",
452 if (args
->exec_state
!= ETNA_PIPE_3D
&&
453 args
->exec_state
!= ETNA_PIPE_2D
&&
454 args
->exec_state
!= ETNA_PIPE_VG
) {
455 DRM_ERROR("invalid exec_state: 0x%x\n", args
->exec_state
);
459 if (args
->flags
& ~ETNA_SUBMIT_FLAGS
) {
460 DRM_ERROR("invalid flags: 0x%x\n", args
->flags
);
464 if ((args
->flags
& ETNA_SUBMIT_SOFTPIN
) &&
465 priv
->mmu_global
->version
!= ETNAVIV_IOMMU_V2
) {
466 DRM_ERROR("softpin requested on incompatible MMU\n");
471 * Copy the command submission and bo array to kernel space in
472 * one go, and do this outside of any locks.
474 bos
= kvmalloc_array(args
->nr_bos
, sizeof(*bos
), GFP_KERNEL
);
475 relocs
= kvmalloc_array(args
->nr_relocs
, sizeof(*relocs
), GFP_KERNEL
);
476 pmrs
= kvmalloc_array(args
->nr_pmrs
, sizeof(*pmrs
), GFP_KERNEL
);
477 stream
= kvmalloc_array(1, args
->stream_size
, GFP_KERNEL
);
478 if (!bos
|| !relocs
|| !pmrs
|| !stream
) {
480 goto err_submit_cmds
;
483 ret
= copy_from_user(bos
, u64_to_user_ptr(args
->bos
),
484 args
->nr_bos
* sizeof(*bos
));
487 goto err_submit_cmds
;
490 ret
= copy_from_user(relocs
, u64_to_user_ptr(args
->relocs
),
491 args
->nr_relocs
* sizeof(*relocs
));
494 goto err_submit_cmds
;
497 ret
= copy_from_user(pmrs
, u64_to_user_ptr(args
->pmrs
),
498 args
->nr_pmrs
* sizeof(*pmrs
));
501 goto err_submit_cmds
;
504 ret
= copy_from_user(stream
, u64_to_user_ptr(args
->stream
),
508 goto err_submit_cmds
;
511 if (args
->flags
& ETNA_SUBMIT_FENCE_FD_OUT
) {
512 out_fence_fd
= get_unused_fd_flags(O_CLOEXEC
);
513 if (out_fence_fd
< 0) {
515 goto err_submit_cmds
;
519 ww_acquire_init(&ticket
, &reservation_ww_class
);
521 submit
= submit_create(dev
, gpu
, args
->nr_bos
, args
->nr_pmrs
);
524 goto err_submit_ww_acquire
;
527 ret
= etnaviv_cmdbuf_init(priv
->cmdbuf_suballoc
, &submit
->cmdbuf
,
528 ALIGN(args
->stream_size
, 8) + 8);
530 goto err_submit_objects
;
532 submit
->ctx
= file
->driver_priv
;
533 etnaviv_iommu_context_get(submit
->ctx
->mmu
);
534 submit
->mmu_context
= submit
->ctx
->mmu
;
535 submit
->exec_state
= args
->exec_state
;
536 submit
->flags
= args
->flags
;
538 ret
= submit_lookup_objects(submit
, file
, bos
, args
->nr_bos
);
540 goto err_submit_objects
;
542 if ((priv
->mmu_global
->version
!= ETNAVIV_IOMMU_V2
) &&
543 !etnaviv_cmd_validate_one(gpu
, stream
, args
->stream_size
/ 4,
544 relocs
, args
->nr_relocs
)) {
546 goto err_submit_objects
;
549 if (args
->flags
& ETNA_SUBMIT_FENCE_FD_IN
) {
550 submit
->in_fence
= sync_file_get_fence(args
->fence_fd
);
551 if (!submit
->in_fence
) {
553 goto err_submit_objects
;
557 ret
= submit_pin_objects(submit
);
559 goto err_submit_objects
;
561 ret
= submit_reloc(submit
, stream
, args
->stream_size
/ 4,
562 relocs
, args
->nr_relocs
);
564 goto err_submit_objects
;
566 ret
= submit_perfmon_validate(submit
, args
->exec_state
, pmrs
);
568 goto err_submit_objects
;
570 memcpy(submit
->cmdbuf
.vaddr
, stream
, args
->stream_size
);
572 ret
= submit_lock_objects(submit
, &ticket
);
574 goto err_submit_objects
;
576 ret
= submit_fence_sync(submit
);
578 goto err_submit_objects
;
580 ret
= etnaviv_sched_push_job(&ctx
->sched_entity
[args
->pipe
], submit
);
582 goto err_submit_objects
;
584 submit_attach_object_fences(submit
);
586 if (args
->flags
& ETNA_SUBMIT_FENCE_FD_OUT
) {
588 * This can be improved: ideally we want to allocate the sync
589 * file before kicking off the GPU job and just attach the
590 * fence to the sync file here, eliminating the ENOMEM
591 * possibility at this stage.
593 sync_file
= sync_file_create(submit
->out_fence
);
596 goto err_submit_objects
;
598 fd_install(out_fence_fd
, sync_file
->file
);
601 args
->fence_fd
= out_fence_fd
;
602 args
->fence
= submit
->out_fence_id
;
605 etnaviv_submit_put(submit
);
607 err_submit_ww_acquire
:
608 ww_acquire_fini(&ticket
);
611 if (ret
&& (out_fence_fd
>= 0))
612 put_unused_fd(out_fence_fd
);