linux/drivers/gpu/drm/msm/msm_gem_submit.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) 2013 Red Hat
   4 * Author: Rob Clark <robdclark@gmail.com>
   5 */
   6
   7#include <linux/file.h>
   8#include <linux/sync_file.h>
   9#include <linux/uaccess.h>
  10
  11#include <drm/drm_drv.h>
  12#include <drm/drm_file.h>
  13#include <drm/drm_syncobj.h>
  14
  15#include "msm_drv.h"
  16#include "msm_gpu.h"
  17#include "msm_gem.h"
  18#include "msm_gpu_trace.h"
  19
  20/*
  21 * Cmdstream submission:
  22 */
  23
  24/* make sure these don't conflict w/ MSM_SUBMIT_BO_x */
  25#define BO_VALID    0x8000   /* is current addr in cmdstream correct/valid? */
  26#define BO_LOCKED   0x4000
  27#define BO_PINNED   0x2000
  28
  29static struct msm_gem_submit *submit_create(struct drm_device *dev,
  30                struct msm_gpu *gpu, struct msm_gem_address_space *aspace,
  31                struct msm_gpu_submitqueue *queue, uint32_t nr_bos,
  32                uint32_t nr_cmds)
  33{
  34        struct msm_gem_submit *submit;
  35        uint64_t sz = struct_size(submit, bos, nr_bos) +
  36                                  ((u64)nr_cmds * sizeof(submit->cmd[0]));
  37
  38        if (sz > SIZE_MAX)
  39                return NULL;
  40
  41        submit = kmalloc(sz, GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY);
  42        if (!submit)
  43                return NULL;
  44
  45        submit->dev = dev;
  46        submit->aspace = aspace;
  47        submit->gpu = gpu;
  48        submit->fence = NULL;
  49        submit->cmd = (void *)&submit->bos[nr_bos];
  50        submit->queue = queue;
  51        submit->ring = gpu->rb[queue->prio];
  52
  53        /* initially, until copy_from_user() and bo lookup succeeds: */
  54        submit->nr_bos = 0;
  55        submit->nr_cmds = 0;
  56
  57        INIT_LIST_HEAD(&submit->node);
  58        INIT_LIST_HEAD(&submit->bo_list);
  59
  60        return submit;
  61}
  62
  63void msm_gem_submit_free(struct msm_gem_submit *submit)
  64{
  65        dma_fence_put(submit->fence);
  66        list_del(&submit->node);
  67        put_pid(submit->pid);
  68        msm_submitqueue_put(submit->queue);
  69
  70        kfree(submit);
  71}
  72
  73static int submit_lookup_objects(struct msm_gem_submit *submit,
  74                struct drm_msm_gem_submit *args, struct drm_file *file)
  75{
  76        unsigned i;
  77        int ret = 0;
  78
  79        for (i = 0; i < args->nr_bos; i++) {
  80                struct drm_msm_gem_submit_bo submit_bo;
  81                void __user *userptr =
  82                        u64_to_user_ptr(args->bos + (i * sizeof(submit_bo)));
  83
  84                /* make sure we don't have garbage flags, in case we hit
  85                 * error path before flags is initialized:
  86                 */
  87                submit->bos[i].flags = 0;
  88
  89                if (copy_from_user(&submit_bo, userptr, sizeof(submit_bo))) {
  90                        ret = -EFAULT;
  91                        i = 0;
  92                        goto out;
  93                }
  94
  95/* at least one of READ and/or WRITE flags should be set: */
  96#define MANDATORY_FLAGS (MSM_SUBMIT_BO_READ | MSM_SUBMIT_BO_WRITE)
  97
  98                if ((submit_bo.flags & ~MSM_SUBMIT_BO_FLAGS) ||
  99                        !(submit_bo.flags & MANDATORY_FLAGS)) {
 100                        DRM_ERROR("invalid flags: %x\n", submit_bo.flags);
 101                        ret = -EINVAL;
 102                        i = 0;
 103                        goto out;
 104                }
 105
 106                submit->bos[i].handle = submit_bo.handle;
 107                submit->bos[i].flags = submit_bo.flags;
 108                /* in validate_objects() we figure out if this is true: */
 109                submit->bos[i].iova  = submit_bo.presumed;
 110        }
 111
 112        spin_lock(&file->table_lock);
 113
 114        for (i = 0; i < args->nr_bos; i++) {
 115                struct drm_gem_object *obj;
 116                struct msm_gem_object *msm_obj;
 117
 118                /* normally use drm_gem_object_lookup(), but for bulk lookup
 119                 * all under single table_lock just hit object_idr directly:
 120                 */
 121                obj = idr_find(&file->object_idr, submit->bos[i].handle);
 122                if (!obj) {
 123                        DRM_ERROR("invalid handle %u at index %u\n", submit->bos[i].handle, i);
 124                        ret = -EINVAL;
 125                        goto out_unlock;
 126                }
 127
 128                msm_obj = to_msm_bo(obj);
 129
 130                if (!list_empty(&msm_obj->submit_entry)) {
 131                        DRM_ERROR("handle %u at index %u already on submit list\n",
 132                                        submit->bos[i].handle, i);
 133                        ret = -EINVAL;
 134                        goto out_unlock;
 135                }
 136
 137                drm_gem_object_get(obj);
 138
 139                submit->bos[i].obj = msm_obj;
 140
 141                list_add_tail(&msm_obj->submit_entry, &submit->bo_list);
 142        }
 143
 144out_unlock:
 145        spin_unlock(&file->table_lock);
 146
 147out:
 148        submit->nr_bos = i;
 149
 150        return ret;
 151}
 152
 153static void submit_unlock_unpin_bo(struct msm_gem_submit *submit,
 154                int i, bool backoff)
 155{
 156        struct msm_gem_object *msm_obj = submit->bos[i].obj;
 157
 158        if (submit->bos[i].flags & BO_PINNED)
 159                msm_gem_unpin_iova(&msm_obj->base, submit->aspace);
 160
 161        if (submit->bos[i].flags & BO_LOCKED)
 162                dma_resv_unlock(msm_obj->base.resv);
 163
 164        if (backoff && !(submit->bos[i].flags & BO_VALID))
 165                submit->bos[i].iova = 0;
 166
 167        submit->bos[i].flags &= ~(BO_LOCKED | BO_PINNED);
 168}
 169
 170/* This is where we make sure all the bo's are reserved and pin'd: */
 171static int submit_lock_objects(struct msm_gem_submit *submit)
 172{
 173        int contended, slow_locked = -1, i, ret = 0;
 174
 175retry:
 176        for (i = 0; i < submit->nr_bos; i++) {
 177                struct msm_gem_object *msm_obj = submit->bos[i].obj;
 178
 179                if (slow_locked == i)
 180                        slow_locked = -1;
 181
 182                contended = i;
 183
 184                if (!(submit->bos[i].flags & BO_LOCKED)) {
 185                        ret = dma_resv_lock_interruptible(msm_obj->base.resv,
 186                                                          &submit->ticket);
 187                        if (ret)
 188                                goto fail;
 189                        submit->bos[i].flags |= BO_LOCKED;
 190                }
 191        }
 192
 193        ww_acquire_done(&submit->ticket);
 194
 195        return 0;
 196
 197fail:
 198        for (; i >= 0; i--)
 199                submit_unlock_unpin_bo(submit, i, true);
 200
 201        if (slow_locked > 0)
 202                submit_unlock_unpin_bo(submit, slow_locked, true);
 203
 204        if (ret == -EDEADLK) {
 205                struct msm_gem_object *msm_obj = submit->bos[contended].obj;
 206                /* we lost out in a seqno race, lock and retry.. */
 207                ret = dma_resv_lock_slow_interruptible(msm_obj->base.resv,
 208                                                       &submit->ticket);
 209                if (!ret) {
 210                        submit->bos[contended].flags |= BO_LOCKED;
 211                        slow_locked = contended;
 212                        goto retry;
 213                }
 214        }
 215
 216        return ret;
 217}
 218
 219static int submit_fence_sync(struct msm_gem_submit *submit, bool no_implicit)
 220{
 221        int i, ret = 0;
 222
 223        for (i = 0; i < submit->nr_bos; i++) {
 224                struct msm_gem_object *msm_obj = submit->bos[i].obj;
 225                bool write = submit->bos[i].flags & MSM_SUBMIT_BO_WRITE;
 226
 227                if (!write) {
 228                        /* NOTE: _reserve_shared() must happen before
 229                         * _add_shared_fence(), which makes this a slightly
 230                         * strange place to call it.  OTOH this is a
 231                         * convenient can-fail point to hook it in.
 232                         */
 233                        ret = dma_resv_reserve_shared(msm_obj->base.resv,
 234                                                                1);
 235                        if (ret)
 236                                return ret;
 237                }
 238
 239                if (no_implicit)
 240                        continue;
 241
 242                ret = msm_gem_sync_object(&msm_obj->base, submit->ring->fctx,
 243                        write);
 244                if (ret)
 245                        break;
 246        }
 247
 248        return ret;
 249}
 250
 251static int submit_pin_objects(struct msm_gem_submit *submit)
 252{
 253        int i, ret = 0;
 254
 255        submit->valid = true;
 256
 257        for (i = 0; i < submit->nr_bos; i++) {
 258                struct msm_gem_object *msm_obj = submit->bos[i].obj;
 259                uint64_t iova;
 260
 261                /* if locking succeeded, pin bo: */
 262                ret = msm_gem_get_and_pin_iova(&msm_obj->base,
 263                                submit->aspace, &iova);
 264
 265                if (ret)
 266                        break;
 267
 268                submit->bos[i].flags |= BO_PINNED;
 269
 270                if (iova == submit->bos[i].iova) {
 271                        submit->bos[i].flags |= BO_VALID;
 272                } else {
 273                        submit->bos[i].iova = iova;
 274                        /* iova changed, so address in cmdstream is not valid: */
 275                        submit->bos[i].flags &= ~BO_VALID;
 276                        submit->valid = false;
 277                }
 278        }
 279
 280        return ret;
 281}
 282
 283static int submit_bo(struct msm_gem_submit *submit, uint32_t idx,
 284                struct msm_gem_object **obj, uint64_t *iova, bool *valid)
 285{
 286        if (idx >= submit->nr_bos) {
 287                DRM_ERROR("invalid buffer index: %u (out of %u)\n",
 288                                idx, submit->nr_bos);
 289                return -EINVAL;
 290        }
 291
 292        if (obj)
 293                *obj = submit->bos[idx].obj;
 294        if (iova)
 295                *iova = submit->bos[idx].iova;
 296        if (valid)
 297                *valid = !!(submit->bos[idx].flags & BO_VALID);
 298
 299        return 0;
 300}
 301
 302/* process the reloc's and patch up the cmdstream as needed: */
 303static int submit_reloc(struct msm_gem_submit *submit, struct msm_gem_object *obj,
 304                uint32_t offset, uint32_t nr_relocs, uint64_t relocs)
 305{
 306        uint32_t i, last_offset = 0;
 307        uint32_t *ptr;
 308        int ret = 0;
 309
 310        if (!nr_relocs)
 311                return 0;
 312
 313        if (offset % 4) {
 314                DRM_ERROR("non-aligned cmdstream buffer: %u\n", offset);
 315                return -EINVAL;
 316        }
 317
 318        /* For now, just map the entire thing.  Eventually we probably
 319         * to do it page-by-page, w/ kmap() if not vmap()d..
 320         */
 321        ptr = msm_gem_get_vaddr(&obj->base);
 322
 323        if (IS_ERR(ptr)) {
 324                ret = PTR_ERR(ptr);
 325                DBG("failed to map: %d", ret);
 326                return ret;
 327        }
 328
 329        for (i = 0; i < nr_relocs; i++) {
 330                struct drm_msm_gem_submit_reloc submit_reloc;
 331                void __user *userptr =
 332                        u64_to_user_ptr(relocs + (i * sizeof(submit_reloc)));
 333                uint32_t off;
 334                uint64_t iova;
 335                bool valid;
 336
 337                if (copy_from_user(&submit_reloc, userptr, sizeof(submit_reloc))) {
 338                        ret = -EFAULT;
 339                        goto out;
 340                }
 341
 342                if (submit_reloc.submit_offset % 4) {
 343                        DRM_ERROR("non-aligned reloc offset: %u\n",
 344                                        submit_reloc.submit_offset);
 345                        ret = -EINVAL;
 346                        goto out;
 347                }
 348
 349                /* offset in dwords: */
 350                off = submit_reloc.submit_offset / 4;
 351
 352                if ((off >= (obj->base.size / 4)) ||
 353                                (off < last_offset)) {
 354                        DRM_ERROR("invalid offset %u at reloc %u\n", off, i);
 355                        ret = -EINVAL;
 356                        goto out;
 357                }
 358
 359                ret = submit_bo(submit, submit_reloc.reloc_idx, NULL, &iova, &valid);
 360                if (ret)
 361                        goto out;
 362
 363                if (valid)
 364                        continue;
 365
 366                iova += submit_reloc.reloc_offset;
 367
 368                if (submit_reloc.shift < 0)
 369                        iova >>= -submit_reloc.shift;
 370                else
 371                        iova <<= submit_reloc.shift;
 372
 373                ptr[off] = iova | submit_reloc.or;
 374
 375                last_offset = off;
 376        }
 377
 378out:
 379        msm_gem_put_vaddr(&obj->base);
 380
 381        return ret;
 382}
 383
 384static void submit_cleanup(struct msm_gem_submit *submit)
 385{
 386        unsigned i;
 387
 388        for (i = 0; i < submit->nr_bos; i++) {
 389                struct msm_gem_object *msm_obj = submit->bos[i].obj;
 390                submit_unlock_unpin_bo(submit, i, false);
 391                list_del_init(&msm_obj->submit_entry);
 392                drm_gem_object_put(&msm_obj->base);
 393        }
 394}
 395
 396
 397struct msm_submit_post_dep {
 398        struct drm_syncobj *syncobj;
 399        uint64_t point;
 400        struct dma_fence_chain *chain;
 401};
 402
 403static struct drm_syncobj **msm_wait_deps(struct drm_device *dev,
 404                                          struct drm_file *file,
 405                                          uint64_t in_syncobjs_addr,
 406                                          uint32_t nr_in_syncobjs,
 407                                          size_t syncobj_stride,
 408                                          struct msm_ringbuffer *ring)
 409{
 410        struct drm_syncobj **syncobjs = NULL;
 411        struct drm_msm_gem_submit_syncobj syncobj_desc = {0};
 412        int ret = 0;
 413        uint32_t i, j;
 414
 415        syncobjs = kcalloc(nr_in_syncobjs, sizeof(*syncobjs),
 416                           GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY);
 417        if (!syncobjs)
 418                return ERR_PTR(-ENOMEM);
 419
 420        for (i = 0; i < nr_in_syncobjs; ++i) {
 421                uint64_t address = in_syncobjs_addr + i * syncobj_stride;
 422                struct dma_fence *fence;
 423
 424                if (copy_from_user(&syncobj_desc,
 425                                   u64_to_user_ptr(address),
 426                                   min(syncobj_stride, sizeof(syncobj_desc)))) {
 427                        ret = -EFAULT;
 428                        break;
 429                }
 430
 431                if (syncobj_desc.point &&
 432                    !drm_core_check_feature(dev, DRIVER_SYNCOBJ_TIMELINE)) {
 433                        ret = -EOPNOTSUPP;
 434                        break;
 435                }
 436
 437                if (syncobj_desc.flags & ~MSM_SUBMIT_SYNCOBJ_FLAGS) {
 438                        ret = -EINVAL;
 439                        break;
 440                }
 441
 442                ret = drm_syncobj_find_fence(file, syncobj_desc.handle,
 443                                             syncobj_desc.point, 0, &fence);
 444                if (ret)
 445                        break;
 446
 447                if (!dma_fence_match_context(fence, ring->fctx->context))
 448                        ret = dma_fence_wait(fence, true);
 449
 450                dma_fence_put(fence);
 451                if (ret)
 452                        break;
 453
 454                if (syncobj_desc.flags & MSM_SUBMIT_SYNCOBJ_RESET) {
 455                        syncobjs[i] =
 456                                drm_syncobj_find(file, syncobj_desc.handle);
 457                        if (!syncobjs[i]) {
 458                                ret = -EINVAL;
 459                                break;
 460                        }
 461                }
 462        }
 463
 464        if (ret) {
 465                for (j = 0; j <= i; ++j) {
 466                        if (syncobjs[j])
 467                                drm_syncobj_put(syncobjs[j]);
 468                }
 469                kfree(syncobjs);
 470                return ERR_PTR(ret);
 471        }
 472        return syncobjs;
 473}
 474
 475static void msm_reset_syncobjs(struct drm_syncobj **syncobjs,
 476                               uint32_t nr_syncobjs)
 477{
 478        uint32_t i;
 479
 480        for (i = 0; syncobjs && i < nr_syncobjs; ++i) {
 481                if (syncobjs[i])
 482                        drm_syncobj_replace_fence(syncobjs[i], NULL);
 483        }
 484}
 485
 486static struct msm_submit_post_dep *msm_parse_post_deps(struct drm_device *dev,
 487                                                       struct drm_file *file,
 488                                                       uint64_t syncobjs_addr,
 489                                                       uint32_t nr_syncobjs,
 490                                                       size_t syncobj_stride)
 491{
 492        struct msm_submit_post_dep *post_deps;
 493        struct drm_msm_gem_submit_syncobj syncobj_desc = {0};
 494        int ret = 0;
 495        uint32_t i, j;
 496
 497        post_deps = kmalloc_array(nr_syncobjs, sizeof(*post_deps),
 498                                  GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY);
 499        if (!post_deps)
 500                return ERR_PTR(-ENOMEM);
 501
 502        for (i = 0; i < nr_syncobjs; ++i) {
 503                uint64_t address = syncobjs_addr + i * syncobj_stride;
 504
 505                if (copy_from_user(&syncobj_desc,
 506                                   u64_to_user_ptr(address),
 507                                   min(syncobj_stride, sizeof(syncobj_desc)))) {
 508                        ret = -EFAULT;
 509                        break;
 510                }
 511
 512                post_deps[i].point = syncobj_desc.point;
 513                post_deps[i].chain = NULL;
 514
 515                if (syncobj_desc.flags) {
 516                        ret = -EINVAL;
 517                        break;
 518                }
 519
 520                if (syncobj_desc.point) {
 521                        if (!drm_core_check_feature(dev,
 522                                                    DRIVER_SYNCOBJ_TIMELINE)) {
 523                                ret = -EOPNOTSUPP;
 524                                break;
 525                        }
 526
 527                        post_deps[i].chain =
 528                                kmalloc(sizeof(*post_deps[i].chain),
 529                                        GFP_KERNEL);
 530                        if (!post_deps[i].chain) {
 531                                ret = -ENOMEM;
 532                                break;
 533                        }
 534                }
 535
 536                post_deps[i].syncobj =
 537                        drm_syncobj_find(file, syncobj_desc.handle);
 538                if (!post_deps[i].syncobj) {
 539                        ret = -EINVAL;
 540                        break;
 541                }
 542        }
 543
 544        if (ret) {
 545                for (j = 0; j <= i; ++j) {
 546                        kfree(post_deps[j].chain);
 547                        if (post_deps[j].syncobj)
 548                                drm_syncobj_put(post_deps[j].syncobj);
 549                }
 550
 551                kfree(post_deps);
 552                return ERR_PTR(ret);
 553        }
 554
 555        return post_deps;
 556}
 557
 558static void msm_process_post_deps(struct msm_submit_post_dep *post_deps,
 559                                  uint32_t count, struct dma_fence *fence)
 560{
 561        uint32_t i;
 562
 563        for (i = 0; post_deps && i < count; ++i) {
 564                if (post_deps[i].chain) {
 565                        drm_syncobj_add_point(post_deps[i].syncobj,
 566                                              post_deps[i].chain,
 567                                              fence, post_deps[i].point);
 568                        post_deps[i].chain = NULL;
 569                } else {
 570                        drm_syncobj_replace_fence(post_deps[i].syncobj,
 571                                                  fence);
 572                }
 573        }
 574}
 575
 576int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
 577                struct drm_file *file)
 578{
 579        static atomic_t ident = ATOMIC_INIT(0);
 580        struct msm_drm_private *priv = dev->dev_private;
 581        struct drm_msm_gem_submit *args = data;
 582        struct msm_file_private *ctx = file->driver_priv;
 583        struct msm_gem_submit *submit;
 584        struct msm_gpu *gpu = priv->gpu;
 585        struct sync_file *sync_file = NULL;
 586        struct msm_gpu_submitqueue *queue;
 587        struct msm_ringbuffer *ring;
 588        struct msm_submit_post_dep *post_deps = NULL;
 589        struct drm_syncobj **syncobjs_to_reset = NULL;
 590        int out_fence_fd = -1;
 591        struct pid *pid = get_pid(task_pid(current));
 592        bool has_ww_ticket = false;
 593        unsigned i;
 594        int ret, submitid;
 595        if (!gpu)
 596                return -ENXIO;
 597
 598        if (args->pad)
 599                return -EINVAL;
 600
 601        /* for now, we just have 3d pipe.. eventually this would need to
 602         * be more clever to dispatch to appropriate gpu module:
 603         */
 604        if (MSM_PIPE_ID(args->flags) != MSM_PIPE_3D0)
 605                return -EINVAL;
 606
 607        if (MSM_PIPE_FLAGS(args->flags) & ~MSM_SUBMIT_FLAGS)
 608                return -EINVAL;
 609
 610        if (args->flags & MSM_SUBMIT_SUDO) {
 611                if (!IS_ENABLED(CONFIG_DRM_MSM_GPU_SUDO) ||
 612                    !capable(CAP_SYS_RAWIO))
 613                        return -EINVAL;
 614        }
 615
 616        queue = msm_submitqueue_get(ctx, args->queueid);
 617        if (!queue)
 618                return -ENOENT;
 619
 620        /* Get a unique identifier for the submission for logging purposes */
 621        submitid = atomic_inc_return(&ident) - 1;
 622
 623        ring = gpu->rb[queue->prio];
 624        trace_msm_gpu_submit(pid_nr(pid), ring->id, submitid,
 625                args->nr_bos, args->nr_cmds);
 626
 627        if (args->flags & MSM_SUBMIT_FENCE_FD_IN) {
 628                struct dma_fence *in_fence;
 629
 630                in_fence = sync_file_get_fence(args->fence_fd);
 631
 632                if (!in_fence)
 633                        return -EINVAL;
 634
 635                /*
 636                 * Wait if the fence is from a foreign context, or if the fence
 637                 * array contains any fence from a foreign context.
 638                 */
 639                ret = 0;
 640                if (!dma_fence_match_context(in_fence, ring->fctx->context))
 641                        ret = dma_fence_wait(in_fence, true);
 642
 643                dma_fence_put(in_fence);
 644                if (ret)
 645                        return ret;
 646        }
 647
 648        if (args->flags & MSM_SUBMIT_SYNCOBJ_IN) {
 649                syncobjs_to_reset = msm_wait_deps(dev, file,
 650                                                  args->in_syncobjs,
 651                                                  args->nr_in_syncobjs,
 652                                                  args->syncobj_stride, ring);
 653                if (IS_ERR(syncobjs_to_reset))
 654                        return PTR_ERR(syncobjs_to_reset);
 655        }
 656
 657        if (args->flags & MSM_SUBMIT_SYNCOBJ_OUT) {
 658                post_deps = msm_parse_post_deps(dev, file,
 659                                                args->out_syncobjs,
 660                                                args->nr_out_syncobjs,
 661                                                args->syncobj_stride);
 662                if (IS_ERR(post_deps)) {
 663                        ret = PTR_ERR(post_deps);
 664                        goto out_post_unlock;
 665                }
 666        }
 667
 668        ret = mutex_lock_interruptible(&dev->struct_mutex);
 669        if (ret)
 670                goto out_post_unlock;
 671
 672        if (args->flags & MSM_SUBMIT_FENCE_FD_OUT) {
 673                out_fence_fd = get_unused_fd_flags(O_CLOEXEC);
 674                if (out_fence_fd < 0) {
 675                        ret = out_fence_fd;
 676                        goto out_unlock;
 677                }
 678        }
 679
 680        submit = submit_create(dev, gpu, ctx->aspace, queue, args->nr_bos,
 681                args->nr_cmds);
 682        if (!submit) {
 683                ret = -ENOMEM;
 684                goto out_unlock;
 685        }
 686
 687        submit->pid = pid;
 688        submit->ident = submitid;
 689
 690        if (args->flags & MSM_SUBMIT_SUDO)
 691                submit->in_rb = true;
 692
 693        ret = submit_lookup_objects(submit, args, file);
 694        if (ret)
 695                goto out;
 696
 697        /* copy_*_user while holding a ww ticket upsets lockdep */
 698        ww_acquire_init(&submit->ticket, &reservation_ww_class);
 699        has_ww_ticket = true;
 700        ret = submit_lock_objects(submit);
 701        if (ret)
 702                goto out;
 703
 704        ret = submit_fence_sync(submit, !!(args->flags & MSM_SUBMIT_NO_IMPLICIT));
 705        if (ret)
 706                goto out;
 707
 708        ret = submit_pin_objects(submit);
 709        if (ret)
 710                goto out;
 711
 712        for (i = 0; i < args->nr_cmds; i++) {
 713                struct drm_msm_gem_submit_cmd submit_cmd;
 714                void __user *userptr =
 715                        u64_to_user_ptr(args->cmds + (i * sizeof(submit_cmd)));
 716                struct msm_gem_object *msm_obj;
 717                uint64_t iova;
 718
 719                ret = copy_from_user(&submit_cmd, userptr, sizeof(submit_cmd));
 720                if (ret) {
 721                        ret = -EFAULT;
 722                        goto out;
 723                }
 724
 725                /* validate input from userspace: */
 726                switch (submit_cmd.type) {
 727                case MSM_SUBMIT_CMD_BUF:
 728                case MSM_SUBMIT_CMD_IB_TARGET_BUF:
 729                case MSM_SUBMIT_CMD_CTX_RESTORE_BUF:
 730                        break;
 731                default:
 732                        DRM_ERROR("invalid type: %08x\n", submit_cmd.type);
 733                        ret = -EINVAL;
 734                        goto out;
 735                }
 736
 737                ret = submit_bo(submit, submit_cmd.submit_idx,
 738                                &msm_obj, &iova, NULL);
 739                if (ret)
 740                        goto out;
 741
 742                if (submit_cmd.size % 4) {
 743                        DRM_ERROR("non-aligned cmdstream buffer size: %u\n",
 744                                        submit_cmd.size);
 745                        ret = -EINVAL;
 746                        goto out;
 747                }
 748
 749                if (!submit_cmd.size ||
 750                        ((submit_cmd.size + submit_cmd.submit_offset) >
 751                                msm_obj->base.size)) {
 752                        DRM_ERROR("invalid cmdstream size: %u\n", submit_cmd.size);
 753                        ret = -EINVAL;
 754                        goto out;
 755                }
 756
 757                submit->cmd[i].type = submit_cmd.type;
 758                submit->cmd[i].size = submit_cmd.size / 4;
 759                submit->cmd[i].iova = iova + submit_cmd.submit_offset;
 760                submit->cmd[i].idx  = submit_cmd.submit_idx;
 761
 762                if (submit->valid)
 763                        continue;
 764
 765                ret = submit_reloc(submit, msm_obj, submit_cmd.submit_offset,
 766                                submit_cmd.nr_relocs, submit_cmd.relocs);
 767                if (ret)
 768                        goto out;
 769        }
 770
 771        submit->nr_cmds = i;
 772
 773        submit->fence = msm_fence_alloc(ring->fctx);
 774        if (IS_ERR(submit->fence)) {
 775                ret = PTR_ERR(submit->fence);
 776                submit->fence = NULL;
 777                goto out;
 778        }
 779
 780        if (args->flags & MSM_SUBMIT_FENCE_FD_OUT) {
 781                sync_file = sync_file_create(submit->fence);
 782                if (!sync_file) {
 783                        ret = -ENOMEM;
 784                        goto out;
 785                }
 786        }
 787
 788        msm_gpu_submit(gpu, submit, ctx);
 789
 790        args->fence = submit->fence->seqno;
 791
 792        if (args->flags & MSM_SUBMIT_FENCE_FD_OUT) {
 793                fd_install(out_fence_fd, sync_file->file);
 794                args->fence_fd = out_fence_fd;
 795        }
 796
 797        msm_reset_syncobjs(syncobjs_to_reset, args->nr_in_syncobjs);
 798        msm_process_post_deps(post_deps, args->nr_out_syncobjs,
 799                              submit->fence);
 800
 801
 802out:
 803        submit_cleanup(submit);
 804        if (has_ww_ticket)
 805                ww_acquire_fini(&submit->ticket);
 806        if (ret)
 807                msm_gem_submit_free(submit);
 808out_unlock:
 809        if (ret && (out_fence_fd >= 0))
 810                put_unused_fd(out_fence_fd);
 811        mutex_unlock(&dev->struct_mutex);
 812
 813out_post_unlock:
 814        if (!IS_ERR_OR_NULL(post_deps)) {
 815                for (i = 0; i < args->nr_out_syncobjs; ++i) {
 816                        kfree(post_deps[i].chain);
 817                        drm_syncobj_put(post_deps[i].syncobj);
 818                }
 819                kfree(post_deps);
 820        }
 821
 822        if (!IS_ERR_OR_NULL(syncobjs_to_reset)) {
 823                for (i = 0; i < args->nr_in_syncobjs; ++i) {
 824                        if (syncobjs_to_reset[i])
 825                                drm_syncobj_put(syncobjs_to_reset[i]);
 826                }
 827                kfree(syncobjs_to_reset);
 828        }
 829
 830        return ret;
 831}
 832