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/>.
21 #include "msm_trace.h"
24 * Cmdstream submission:
27 /* make sure these don't conflict w/ MSM_SUBMIT_BO_x */
28 #define BO_VALID 0x8000
29 #define BO_LOCKED 0x4000
30 #define BO_PINNED 0x2000
32 static struct msm_gem_submit *submit_create(struct drm_device *dev,
33 struct msm_gem_address_space *aspace,
34 uint32_t nr_bos, uint32_t nr_cmds,
35 struct msm_gpu_submitqueue *queue)
37 struct msm_gem_submit *submit;
38 uint64_t sz = sizeof(*submit) + ((u64)nr_bos * sizeof(submit->bos[0])) +
39 ((u64)nr_cmds * sizeof(submit->cmd[0]));
44 submit = kmalloc(sz, GFP_TEMPORARY | __GFP_NOWARN | __GFP_NORETRY);
47 submit->aspace = aspace;
48 submit->queue = queue;
50 /* initially, until copy_from_user() and bo lookup succeeds: */
54 submit->profile_buf = NULL;
55 submit->profile_buf_iova = 0;
56 submit->cmd = (void *)&submit->bos[nr_bos];
58 submit->secure = false;
61 * Initalize node so we can safely list_del() on it if
62 * we fail in the submit path
64 INIT_LIST_HEAD(&submit->node);
65 INIT_LIST_HEAD(&submit->bo_list);
66 ww_acquire_init(&submit->ticket, &reservation_ww_class);
72 static inline unsigned long __must_check
73 copy_from_user_inatomic(void *to, const void __user *from, unsigned long n)
75 if (access_ok(VERIFY_READ, from, n))
76 return __copy_from_user_inatomic(to, from, n);
80 void msm_gem_submit_free(struct msm_gem_submit *submit)
85 msm_submitqueue_put(submit->queue);
86 list_del(&submit->node);
90 static int submit_lookup_objects(struct msm_gpu *gpu,
91 struct msm_gem_submit *submit,
92 struct drm_msm_gem_submit *args, struct drm_file *file)
97 spin_lock(&file->table_lock);
100 for (i = 0; i < args->nr_bos; i++) {
101 struct drm_msm_gem_submit_bo submit_bo;
102 struct drm_gem_object *obj;
103 struct msm_gem_object *msm_obj;
104 void __user *userptr =
105 u64_to_user_ptr(args->bos + (i * sizeof(submit_bo)));
107 if (copy_from_user_inatomic(&submit_bo, userptr,
108 sizeof(submit_bo))) {
110 spin_unlock(&file->table_lock);
111 if (copy_from_user(&submit_bo, userptr,
112 sizeof(submit_bo))) {
117 spin_lock(&file->table_lock);
121 if ((submit_bo.flags & ~MSM_SUBMIT_BO_FLAGS) ||
122 !(submit_bo.flags & MSM_SUBMIT_BO_FLAGS)) {
123 DRM_ERROR("invalid flags: %x\n", submit_bo.flags);
128 submit->bos[i].flags = submit_bo.flags;
129 /* in validate_objects() we figure out if this is true: */
130 submit->bos[i].iova = submit_bo.presumed;
132 /* normally use drm_gem_object_lookup(), but for bulk lookup
133 * all under single table_lock just hit object_idr directly:
135 obj = idr_find(&file->object_idr, submit_bo.handle);
137 DRM_ERROR("invalid handle %u at index %u\n", submit_bo.handle, i);
142 msm_obj = to_msm_bo(obj);
145 * If the buffer is marked as secure make sure that we can
146 * handle secure buffers and then mark the submission as secure
148 if (msm_obj->flags & MSM_BO_SECURE) {
149 if (!gpu->secure_aspace) {
150 DRM_ERROR("Cannot handle secure buffers\n");
155 submit->secure = true;
158 if (!list_empty(&msm_obj->submit_entry)) {
159 DRM_ERROR("handle %u at index %u already on submit list\n",
160 submit_bo.handle, i);
165 drm_gem_object_reference(obj);
167 submit->bos[i].obj = msm_obj;
169 list_add_tail(&msm_obj->submit_entry, &submit->bo_list);
174 spin_unlock(&file->table_lock);
182 static void submit_unlock_unpin_bo(struct msm_gpu *gpu,
183 struct msm_gem_submit *submit, int i)
185 struct msm_gem_object *msm_obj = submit->bos[i].obj;
186 struct msm_gem_address_space *aspace;
188 aspace = (msm_obj->flags & MSM_BO_SECURE) ?
189 gpu->secure_aspace : submit->aspace;
191 if (submit->bos[i].flags & BO_PINNED)
192 msm_gem_put_iova(&msm_obj->base, aspace);
194 if (submit->bos[i].flags & BO_LOCKED)
195 ww_mutex_unlock(&msm_obj->resv->lock);
197 if (!(submit->bos[i].flags & BO_VALID))
198 submit->bos[i].iova = 0;
200 submit->bos[i].flags &= ~(BO_LOCKED | BO_PINNED);
203 /* This is where we make sure all the bo's are reserved and pin'd: */
204 static int submit_validate_objects(struct msm_gpu *gpu,
205 struct msm_gem_submit *submit)
207 int contended, slow_locked = -1, i, ret = 0;
210 for (i = 0; i < submit->nr_bos; i++) {
211 struct msm_gem_object *msm_obj = submit->bos[i].obj;
213 if (slow_locked == i)
218 if (!(submit->bos[i].flags & BO_LOCKED)) {
219 ret = ww_mutex_lock_interruptible(&msm_obj->resv->lock,
223 submit->bos[i].flags |= BO_LOCKED;
227 * An invalid SVM object is part of
228 * this submit's buffer list, fail.
230 if (msm_obj->flags & MSM_BO_SVM) {
231 struct msm_gem_svm_object *msm_svm_obj =
232 to_msm_svm_obj(msm_obj);
233 if (msm_svm_obj->invalid) {
240 ww_acquire_done(&submit->ticket);
246 submit_unlock_unpin_bo(gpu, submit, i);
249 submit_unlock_unpin_bo(gpu, submit, slow_locked);
251 if (ret == -EDEADLK) {
252 struct msm_gem_object *msm_obj = submit->bos[contended].obj;
253 /* we lost out in a seqno race, lock and retry.. */
254 ret = ww_mutex_lock_slow_interruptible(&msm_obj->resv->lock,
257 submit->bos[contended].flags |= BO_LOCKED;
258 slow_locked = contended;
266 static int submit_bo(struct msm_gpu *gpu,
267 struct msm_gem_submit *submit, uint32_t idx,
268 struct msm_gem_object **obj, uint64_t *iova, bool *valid)
270 struct msm_gem_object *msm_obj;
271 struct msm_gem_address_space *aspace;
274 if (idx >= submit->nr_bos) {
275 DRM_ERROR("invalid buffer index: %u (out of %u)\n",
276 idx, submit->nr_bos);
281 *obj = submit->bos[idx].obj;
283 /* Only map and pin if the caller needs either the iova or valid */
287 if (!(submit->bos[idx].flags & BO_PINNED)) {
290 msm_obj = submit->bos[idx].obj;
291 aspace = (msm_obj->flags & MSM_BO_SECURE) ?
292 gpu->secure_aspace : submit->aspace;
294 ret = msm_gem_get_iova(&msm_obj->base, aspace, &buf_iova);
296 /* this would break the logic in the fail path.. there is no
297 * reason for this to happen, but just to be on the safe side
298 * let's notice if this starts happening in the future:
300 WARN_ON(ret == -EDEADLK);
305 submit->bos[idx].flags |= BO_PINNED;
307 if (buf_iova == submit->bos[idx].iova) {
308 submit->bos[idx].flags |= BO_VALID;
310 submit->bos[idx].iova = buf_iova;
311 submit->bos[idx].flags &= ~BO_VALID;
316 *iova = submit->bos[idx].iova;
318 *valid = !!(submit->bos[idx].flags & BO_VALID);
323 /* process the reloc's and patch up the cmdstream as needed: */
324 static int submit_reloc(struct msm_gpu *gpu,
325 struct msm_gem_submit *submit,
326 struct msm_gem_object *obj, uint32_t offset,
327 uint32_t nr_relocs, uint64_t relocs)
329 uint32_t i, last_offset = 0;
334 DRM_ERROR("non-aligned cmdstream buffer: %u\n", offset);
338 if (obj->flags & MSM_BO_SECURE) {
339 DRM_ERROR("cannot do relocs on a secure buffer\n");
346 /* For now, just map the entire thing. Eventually we probably
347 * to do it page-by-page, w/ kmap() if not vmap()d..
349 ptr = msm_gem_vaddr(&obj->base);
351 DRM_ERROR("Invalid format");
357 DBG("failed to map: %d", ret);
361 for (i = 0; i < nr_relocs; i++) {
362 struct drm_msm_gem_submit_reloc submit_reloc;
363 void __user *userptr =
364 u64_to_user_ptr(relocs + (i * sizeof(submit_reloc)));
369 if (copy_from_user(&submit_reloc, userptr,
370 sizeof(submit_reloc)))
373 if (submit_reloc.submit_offset % 4) {
374 DRM_ERROR("non-aligned reloc offset: %u\n",
375 submit_reloc.submit_offset);
379 /* offset in dwords: */
380 off = submit_reloc.submit_offset / 4;
382 if ((off >= (obj->base.size / 4)) ||
383 (off < last_offset)) {
384 DRM_ERROR("invalid offset %u at reloc %u\n", off, i);
388 ret = submit_bo(gpu, submit, submit_reloc.reloc_idx,
389 NULL, &iova, &valid);
396 iova += submit_reloc.reloc_offset;
398 if (submit_reloc.shift < 0)
399 iova >>= -submit_reloc.shift;
401 iova <<= submit_reloc.shift;
403 ptr[off] = iova | submit_reloc.or;
411 static void submit_cleanup(struct msm_gpu *gpu, struct msm_gem_submit *submit,
419 for (i = 0; i < submit->nr_bos; i++) {
420 struct msm_gem_object *msm_obj = submit->bos[i].obj;
421 submit_unlock_unpin_bo(gpu, submit, i);
422 list_del_init(&msm_obj->submit_entry);
423 drm_gem_object_unreference(&msm_obj->base);
426 ww_acquire_fini(&submit->ticket);
429 int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
430 struct drm_file *file)
432 struct msm_drm_private *priv = dev->dev_private;
433 struct drm_msm_gem_submit *args = data;
434 struct msm_file_private *ctx = file->driver_priv;
435 struct msm_gem_submit *submit;
436 struct msm_gpu_submitqueue *queue;
441 /* for now, we just have 3d pipe.. eventually this would need to
442 * be more clever to dispatch to appropriate gpu module:
444 if (MSM_PIPE_ID(args->flags) != MSM_PIPE_3D0)
451 queue = msm_submitqueue_get(ctx, args->queueid);
455 mutex_lock(&dev->struct_mutex);
457 submit = submit_create(dev, ctx->aspace, args->nr_bos, args->nr_cmds,
464 ret = submit_lookup_objects(gpu, submit, args, file);
468 ret = submit_validate_objects(gpu, submit);
472 for (i = 0; i < args->nr_cmds; i++) {
473 struct drm_msm_gem_submit_cmd submit_cmd;
474 void __user *userptr =
475 u64_to_user_ptr(args->cmds + (i * sizeof(submit_cmd)));
476 struct msm_gem_object *msm_obj;
480 ret = copy_from_user(&submit_cmd, userptr, sizeof(submit_cmd));
486 /* validate input from userspace: */
487 switch (submit_cmd.type) {
488 case MSM_SUBMIT_CMD_BUF:
489 case MSM_SUBMIT_CMD_IB_TARGET_BUF:
490 case MSM_SUBMIT_CMD_CTX_RESTORE_BUF:
491 case MSM_SUBMIT_CMD_PROFILE_BUF:
494 DRM_ERROR("invalid type: %08x\n", submit_cmd.type);
499 ret = submit_bo(gpu, submit, submit_cmd.submit_idx,
500 &msm_obj, &iova, NULL);
504 if (submit_cmd.size % 4) {
505 DRM_ERROR("non-aligned cmdstream buffer size: %u\n",
511 size = submit_cmd.size + submit_cmd.submit_offset;
513 if (!submit_cmd.size || (size < submit_cmd.size) ||
514 (size > msm_obj->base.size)) {
515 DRM_ERROR("invalid cmdstream offset/size: %u/%u\n",
516 submit_cmd.submit_offset, submit_cmd.size);
521 submit->cmd[i].type = submit_cmd.type;
522 submit->cmd[i].size = submit_cmd.size / 4;
523 submit->cmd[i].iova = iova + submit_cmd.submit_offset;
524 submit->cmd[i].idx = submit_cmd.submit_idx;
526 if (submit_cmd.type == MSM_SUBMIT_CMD_PROFILE_BUF) {
527 submit->profile_buf_iova = submit->cmd[i].iova;
528 submit->profile_buf = msm_gem_vaddr(&msm_obj->base)
529 + submit_cmd.submit_offset;
532 ret = submit_reloc(gpu, submit, msm_obj,
533 submit_cmd.submit_offset, submit_cmd.nr_relocs,
541 /* Clamp the user submitted ring to the range of available rings */
542 submit->ring = clamp_t(uint32_t, queue->prio, 0, gpu->nr_rings - 1);
544 ret = msm_gpu_submit(gpu, submit);
546 args->fence = submit->fence;
549 submit_cleanup(gpu, submit, !!ret);
551 msm_gem_submit_free(submit);
552 mutex_unlock(&dev->struct_mutex);