2 * Copyright (C) 2013 Red Hat
3 * Author: Rob Clark <robdclark@gmail.com>
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
18 #include <linux/sync_file.h>
23 #include "msm_gpu_trace.h"
26 * Cmdstream submission:
29 /* make sure these don't conflict w/ MSM_SUBMIT_BO_x */
30 #define BO_VALID 0x8000 /* is current addr in cmdstream correct/valid? */
31 #define BO_LOCKED 0x4000
32 #define BO_PINNED 0x2000
34 static struct msm_gem_submit
*submit_create(struct drm_device
*dev
,
35 struct msm_gpu
*gpu
, struct msm_gpu_submitqueue
*queue
,
36 uint32_t nr_bos
, uint32_t nr_cmds
)
38 struct msm_gem_submit
*submit
;
39 uint64_t sz
= sizeof(*submit
) + ((u64
)nr_bos
* sizeof(submit
->bos
[0])) +
40 ((u64
)nr_cmds
* sizeof(submit
->cmd
[0]));
45 submit
= kmalloc(sz
, GFP_KERNEL
| __GFP_NOWARN
| __GFP_NORETRY
);
52 submit
->cmd
= (void *)&submit
->bos
[nr_bos
];
53 submit
->queue
= queue
;
54 submit
->ring
= gpu
->rb
[queue
->prio
];
56 /* initially, until copy_from_user() and bo lookup succeeds: */
60 INIT_LIST_HEAD(&submit
->node
);
61 INIT_LIST_HEAD(&submit
->bo_list
);
62 ww_acquire_init(&submit
->ticket
, &reservation_ww_class
);
67 void msm_gem_submit_free(struct msm_gem_submit
*submit
)
69 dma_fence_put(submit
->fence
);
70 list_del(&submit
->node
);
72 msm_submitqueue_put(submit
->queue
);
77 static inline unsigned long __must_check
78 copy_from_user_inatomic(void *to
, const void __user
*from
, unsigned long n
)
80 if (access_ok(from
, n
))
81 return __copy_from_user_inatomic(to
, from
, n
);
85 static int submit_lookup_objects(struct msm_gem_submit
*submit
,
86 struct drm_msm_gem_submit
*args
, struct drm_file
*file
)
91 spin_lock(&file
->table_lock
);
94 for (i
= 0; i
< args
->nr_bos
; i
++) {
95 struct drm_msm_gem_submit_bo submit_bo
;
96 struct drm_gem_object
*obj
;
97 struct msm_gem_object
*msm_obj
;
98 void __user
*userptr
=
99 u64_to_user_ptr(args
->bos
+ (i
* sizeof(submit_bo
)));
101 /* make sure we don't have garbage flags, in case we hit
102 * error path before flags is initialized:
104 submit
->bos
[i
].flags
= 0;
106 if (copy_from_user_inatomic(&submit_bo
, userptr
, sizeof(submit_bo
))) {
108 spin_unlock(&file
->table_lock
);
109 if (copy_from_user(&submit_bo
, userptr
, sizeof(submit_bo
))) {
113 spin_lock(&file
->table_lock
);
117 /* at least one of READ and/or WRITE flags should be set: */
118 #define MANDATORY_FLAGS (MSM_SUBMIT_BO_READ | MSM_SUBMIT_BO_WRITE)
120 if ((submit_bo
.flags
& ~MSM_SUBMIT_BO_FLAGS
) ||
121 !(submit_bo
.flags
& MANDATORY_FLAGS
)) {
122 DRM_ERROR("invalid flags: %x\n", submit_bo
.flags
);
127 submit
->bos
[i
].flags
= submit_bo
.flags
;
128 /* in validate_objects() we figure out if this is true: */
129 submit
->bos
[i
].iova
= submit_bo
.presumed
;
131 /* normally use drm_gem_object_lookup(), but for bulk lookup
132 * all under single table_lock just hit object_idr directly:
134 obj
= idr_find(&file
->object_idr
, submit_bo
.handle
);
136 DRM_ERROR("invalid handle %u at index %u\n", submit_bo
.handle
, i
);
141 msm_obj
= to_msm_bo(obj
);
143 if (!list_empty(&msm_obj
->submit_entry
)) {
144 DRM_ERROR("handle %u at index %u already on submit list\n",
145 submit_bo
.handle
, i
);
150 drm_gem_object_get(obj
);
152 submit
->bos
[i
].obj
= msm_obj
;
154 list_add_tail(&msm_obj
->submit_entry
, &submit
->bo_list
);
159 spin_unlock(&file
->table_lock
);
167 static void submit_unlock_unpin_bo(struct msm_gem_submit
*submit
,
170 struct msm_gem_object
*msm_obj
= submit
->bos
[i
].obj
;
172 if (submit
->bos
[i
].flags
& BO_PINNED
)
173 msm_gem_unpin_iova(&msm_obj
->base
, submit
->gpu
->aspace
);
175 if (submit
->bos
[i
].flags
& BO_LOCKED
)
176 ww_mutex_unlock(&msm_obj
->resv
->lock
);
178 if (backoff
&& !(submit
->bos
[i
].flags
& BO_VALID
))
179 submit
->bos
[i
].iova
= 0;
181 submit
->bos
[i
].flags
&= ~(BO_LOCKED
| BO_PINNED
);
184 /* This is where we make sure all the bo's are reserved and pin'd: */
185 static int submit_lock_objects(struct msm_gem_submit
*submit
)
187 int contended
, slow_locked
= -1, i
, ret
= 0;
190 for (i
= 0; i
< submit
->nr_bos
; i
++) {
191 struct msm_gem_object
*msm_obj
= submit
->bos
[i
].obj
;
193 if (slow_locked
== i
)
198 if (!(submit
->bos
[i
].flags
& BO_LOCKED
)) {
199 ret
= ww_mutex_lock_interruptible(&msm_obj
->resv
->lock
,
203 submit
->bos
[i
].flags
|= BO_LOCKED
;
207 ww_acquire_done(&submit
->ticket
);
213 submit_unlock_unpin_bo(submit
, i
, true);
216 submit_unlock_unpin_bo(submit
, slow_locked
, true);
218 if (ret
== -EDEADLK
) {
219 struct msm_gem_object
*msm_obj
= submit
->bos
[contended
].obj
;
220 /* we lost out in a seqno race, lock and retry.. */
221 ret
= ww_mutex_lock_slow_interruptible(&msm_obj
->resv
->lock
,
224 submit
->bos
[contended
].flags
|= BO_LOCKED
;
225 slow_locked
= contended
;
233 static int submit_fence_sync(struct msm_gem_submit
*submit
, bool no_implicit
)
237 for (i
= 0; i
< submit
->nr_bos
; i
++) {
238 struct msm_gem_object
*msm_obj
= submit
->bos
[i
].obj
;
239 bool write
= submit
->bos
[i
].flags
& MSM_SUBMIT_BO_WRITE
;
242 /* NOTE: _reserve_shared() must happen before
243 * _add_shared_fence(), which makes this a slightly
244 * strange place to call it. OTOH this is a
245 * convenient can-fail point to hook it in.
247 ret
= reservation_object_reserve_shared(msm_obj
->resv
,
256 ret
= msm_gem_sync_object(&msm_obj
->base
, submit
->ring
->fctx
,
265 static int submit_pin_objects(struct msm_gem_submit
*submit
)
269 submit
->valid
= true;
271 for (i
= 0; i
< submit
->nr_bos
; i
++) {
272 struct msm_gem_object
*msm_obj
= submit
->bos
[i
].obj
;
275 /* if locking succeeded, pin bo: */
276 ret
= msm_gem_get_and_pin_iova(&msm_obj
->base
,
277 submit
->gpu
->aspace
, &iova
);
282 submit
->bos
[i
].flags
|= BO_PINNED
;
284 if (iova
== submit
->bos
[i
].iova
) {
285 submit
->bos
[i
].flags
|= BO_VALID
;
287 submit
->bos
[i
].iova
= iova
;
288 /* iova changed, so address in cmdstream is not valid: */
289 submit
->bos
[i
].flags
&= ~BO_VALID
;
290 submit
->valid
= false;
297 static int submit_bo(struct msm_gem_submit
*submit
, uint32_t idx
,
298 struct msm_gem_object
**obj
, uint64_t *iova
, bool *valid
)
300 if (idx
>= submit
->nr_bos
) {
301 DRM_ERROR("invalid buffer index: %u (out of %u)\n",
302 idx
, submit
->nr_bos
);
307 *obj
= submit
->bos
[idx
].obj
;
309 *iova
= submit
->bos
[idx
].iova
;
311 *valid
= !!(submit
->bos
[idx
].flags
& BO_VALID
);
316 /* process the reloc's and patch up the cmdstream as needed: */
317 static int submit_reloc(struct msm_gem_submit
*submit
, struct msm_gem_object
*obj
,
318 uint32_t offset
, uint32_t nr_relocs
, uint64_t relocs
)
320 uint32_t i
, last_offset
= 0;
328 DRM_ERROR("non-aligned cmdstream buffer: %u\n", offset
);
332 /* For now, just map the entire thing. Eventually we probably
333 * to do it page-by-page, w/ kmap() if not vmap()d..
335 ptr
= msm_gem_get_vaddr(&obj
->base
);
339 DBG("failed to map: %d", ret
);
343 for (i
= 0; i
< nr_relocs
; i
++) {
344 struct drm_msm_gem_submit_reloc submit_reloc
;
345 void __user
*userptr
=
346 u64_to_user_ptr(relocs
+ (i
* sizeof(submit_reloc
)));
351 if (copy_from_user(&submit_reloc
, userptr
, sizeof(submit_reloc
))) {
356 if (submit_reloc
.submit_offset
% 4) {
357 DRM_ERROR("non-aligned reloc offset: %u\n",
358 submit_reloc
.submit_offset
);
363 /* offset in dwords: */
364 off
= submit_reloc
.submit_offset
/ 4;
366 if ((off
>= (obj
->base
.size
/ 4)) ||
367 (off
< last_offset
)) {
368 DRM_ERROR("invalid offset %u at reloc %u\n", off
, i
);
373 ret
= submit_bo(submit
, submit_reloc
.reloc_idx
, NULL
, &iova
, &valid
);
380 iova
+= submit_reloc
.reloc_offset
;
382 if (submit_reloc
.shift
< 0)
383 iova
>>= -submit_reloc
.shift
;
385 iova
<<= submit_reloc
.shift
;
387 ptr
[off
] = iova
| submit_reloc
.or;
393 msm_gem_put_vaddr(&obj
->base
);
398 static void submit_cleanup(struct msm_gem_submit
*submit
)
402 for (i
= 0; i
< submit
->nr_bos
; i
++) {
403 struct msm_gem_object
*msm_obj
= submit
->bos
[i
].obj
;
404 submit_unlock_unpin_bo(submit
, i
, false);
405 list_del_init(&msm_obj
->submit_entry
);
406 drm_gem_object_put(&msm_obj
->base
);
409 ww_acquire_fini(&submit
->ticket
);
412 int msm_ioctl_gem_submit(struct drm_device
*dev
, void *data
,
413 struct drm_file
*file
)
415 static atomic_t ident
= ATOMIC_INIT(0);
416 struct msm_drm_private
*priv
= dev
->dev_private
;
417 struct drm_msm_gem_submit
*args
= data
;
418 struct msm_file_private
*ctx
= file
->driver_priv
;
419 struct msm_gem_submit
*submit
;
420 struct msm_gpu
*gpu
= priv
->gpu
;
421 struct sync_file
*sync_file
= NULL
;
422 struct msm_gpu_submitqueue
*queue
;
423 struct msm_ringbuffer
*ring
;
424 int out_fence_fd
= -1;
425 struct pid
*pid
= get_pid(task_pid(current
));
431 /* for now, we just have 3d pipe.. eventually this would need to
432 * be more clever to dispatch to appropriate gpu module:
434 if (MSM_PIPE_ID(args
->flags
) != MSM_PIPE_3D0
)
437 if (MSM_PIPE_FLAGS(args
->flags
) & ~MSM_SUBMIT_FLAGS
)
440 if (args
->flags
& MSM_SUBMIT_SUDO
) {
441 if (!IS_ENABLED(CONFIG_DRM_MSM_GPU_SUDO
) ||
442 !capable(CAP_SYS_RAWIO
))
446 queue
= msm_submitqueue_get(ctx
, args
->queueid
);
450 /* Get a unique identifier for the submission for logging purposes */
451 submitid
= atomic_inc_return(&ident
) - 1;
453 ring
= gpu
->rb
[queue
->prio
];
454 trace_msm_gpu_submit(pid_nr(pid
), ring
->id
, submitid
,
455 args
->nr_bos
, args
->nr_cmds
);
457 if (args
->flags
& MSM_SUBMIT_FENCE_FD_IN
) {
458 struct dma_fence
*in_fence
;
460 in_fence
= sync_file_get_fence(args
->fence_fd
);
466 * Wait if the fence is from a foreign context, or if the fence
467 * array contains any fence from a foreign context.
470 if (!dma_fence_match_context(in_fence
, ring
->fctx
->context
))
471 ret
= dma_fence_wait(in_fence
, true);
473 dma_fence_put(in_fence
);
478 ret
= mutex_lock_interruptible(&dev
->struct_mutex
);
482 if (args
->flags
& MSM_SUBMIT_FENCE_FD_OUT
) {
483 out_fence_fd
= get_unused_fd_flags(O_CLOEXEC
);
484 if (out_fence_fd
< 0) {
490 submit
= submit_create(dev
, gpu
, queue
, args
->nr_bos
, args
->nr_cmds
);
497 submit
->ident
= submitid
;
499 if (args
->flags
& MSM_SUBMIT_SUDO
)
500 submit
->in_rb
= true;
502 ret
= submit_lookup_objects(submit
, args
, file
);
506 ret
= submit_lock_objects(submit
);
510 ret
= submit_fence_sync(submit
, !!(args
->flags
& MSM_SUBMIT_NO_IMPLICIT
));
514 ret
= submit_pin_objects(submit
);
518 for (i
= 0; i
< args
->nr_cmds
; i
++) {
519 struct drm_msm_gem_submit_cmd submit_cmd
;
520 void __user
*userptr
=
521 u64_to_user_ptr(args
->cmds
+ (i
* sizeof(submit_cmd
)));
522 struct msm_gem_object
*msm_obj
;
525 ret
= copy_from_user(&submit_cmd
, userptr
, sizeof(submit_cmd
));
531 /* validate input from userspace: */
532 switch (submit_cmd
.type
) {
533 case MSM_SUBMIT_CMD_BUF
:
534 case MSM_SUBMIT_CMD_IB_TARGET_BUF
:
535 case MSM_SUBMIT_CMD_CTX_RESTORE_BUF
:
538 DRM_ERROR("invalid type: %08x\n", submit_cmd
.type
);
543 ret
= submit_bo(submit
, submit_cmd
.submit_idx
,
544 &msm_obj
, &iova
, NULL
);
548 if (submit_cmd
.size
% 4) {
549 DRM_ERROR("non-aligned cmdstream buffer size: %u\n",
555 if (!submit_cmd
.size
||
556 ((submit_cmd
.size
+ submit_cmd
.submit_offset
) >
557 msm_obj
->base
.size
)) {
558 DRM_ERROR("invalid cmdstream size: %u\n", submit_cmd
.size
);
563 submit
->cmd
[i
].type
= submit_cmd
.type
;
564 submit
->cmd
[i
].size
= submit_cmd
.size
/ 4;
565 submit
->cmd
[i
].iova
= iova
+ submit_cmd
.submit_offset
;
566 submit
->cmd
[i
].idx
= submit_cmd
.submit_idx
;
571 ret
= submit_reloc(submit
, msm_obj
, submit_cmd
.submit_offset
,
572 submit_cmd
.nr_relocs
, submit_cmd
.relocs
);
579 submit
->fence
= msm_fence_alloc(ring
->fctx
);
580 if (IS_ERR(submit
->fence
)) {
581 ret
= PTR_ERR(submit
->fence
);
582 submit
->fence
= NULL
;
586 if (args
->flags
& MSM_SUBMIT_FENCE_FD_OUT
) {
587 sync_file
= sync_file_create(submit
->fence
);
594 msm_gpu_submit(gpu
, submit
, ctx
);
596 args
->fence
= submit
->fence
->seqno
;
598 if (args
->flags
& MSM_SUBMIT_FENCE_FD_OUT
) {
599 fd_install(out_fence_fd
, sync_file
->file
);
600 args
->fence_fd
= out_fence_fd
;
604 submit_cleanup(submit
);
606 msm_gem_submit_free(submit
);
608 if (ret
&& (out_fence_fd
>= 0))
609 put_unused_fd(out_fence_fd
);
610 mutex_unlock(&dev
->struct_mutex
);