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   /* obj lock is held */
  27#define BO_ACTIVE   0x2000   /* active refcnt is held */
  28#define BO_PINNED   0x1000   /* obj is pinned and on active list */
  29
  30static struct msm_gem_submit *submit_create(struct drm_device *dev,
  31                struct msm_gpu *gpu,
  32                struct msm_gpu_submitqueue *queue, uint32_t nr_bos,
  33                uint32_t nr_cmds)
  34{
  35        struct msm_gem_submit *submit;
  36        uint64_t sz;
  37        int ret;
  38
  39        sz = struct_size(submit, bos, nr_bos) +
  40                        ((u64)nr_cmds * sizeof(submit->cmd[0]));
  41
  42        if (sz > SIZE_MAX)
  43                return ERR_PTR(-ENOMEM);
  44
  45        submit = kzalloc(sz, GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY);
  46        if (!submit)
  47                return ERR_PTR(-ENOMEM);
  48
  49        ret = drm_sched_job_init(&submit->base, queue->entity, queue);
  50        if (ret) {
  51                kfree(submit);
  52                return ERR_PTR(ret);
  53        }
  54
  55        xa_init_flags(&submit->deps, XA_FLAGS_ALLOC);
  56
  57        kref_init(&submit->ref);
  58        submit->dev = dev;
  59        submit->aspace = queue->ctx->aspace;
  60        submit->gpu = gpu;
  61        submit->cmd = (void *)&submit->bos[nr_bos];
  62        submit->queue = queue;
  63        submit->ring = gpu->rb[queue->ring_nr];
  64        submit->fault_dumped = false;
  65
  66        INIT_LIST_HEAD(&submit->node);
  67
  68        return submit;
  69}
  70
  71void __msm_gem_submit_destroy(struct kref *kref)
  72{
  73        struct msm_gem_submit *submit =
  74                        container_of(kref, struct msm_gem_submit, ref);
  75        unsigned long index;
  76        struct dma_fence *fence;
  77        unsigned i;
  78
  79        if (submit->fence_id) {
  80                mutex_lock(&submit->queue->lock);
  81                idr_remove(&submit->queue->fence_idr, submit->fence_id);
  82                mutex_unlock(&submit->queue->lock);
  83        }
  84
  85        xa_for_each (&submit->deps, index, fence) {
  86                dma_fence_put(fence);
  87        }
  88
  89        xa_destroy(&submit->deps);
  90
  91        dma_fence_put(submit->user_fence);
  92        dma_fence_put(submit->hw_fence);
  93
  94        put_pid(submit->pid);
  95        msm_submitqueue_put(submit->queue);
  96
  97        for (i = 0; i < submit->nr_cmds; i++)
  98                kfree(submit->cmd[i].relocs);
  99
 100        kfree(submit);
 101}
 102
 103static int submit_lookup_objects(struct msm_gem_submit *submit,
 104                struct drm_msm_gem_submit *args, struct drm_file *file)
 105{
 106        unsigned i;
 107        int ret = 0;
 108
 109        for (i = 0; i < args->nr_bos; i++) {
 110                struct drm_msm_gem_submit_bo submit_bo;
 111                void __user *userptr =
 112                        u64_to_user_ptr(args->bos + (i * sizeof(submit_bo)));
 113
 114                /* make sure we don't have garbage flags, in case we hit
 115                 * error path before flags is initialized:
 116                 */
 117                submit->bos[i].flags = 0;
 118
 119                if (copy_from_user(&submit_bo, userptr, sizeof(submit_bo))) {
 120                        ret = -EFAULT;
 121                        i = 0;
 122                        goto out;
 123                }
 124
 125/* at least one of READ and/or WRITE flags should be set: */
 126#define MANDATORY_FLAGS (MSM_SUBMIT_BO_READ | MSM_SUBMIT_BO_WRITE)
 127
 128                if ((submit_bo.flags & ~MSM_SUBMIT_BO_FLAGS) ||
 129                        !(submit_bo.flags & MANDATORY_FLAGS)) {
 130                        DRM_ERROR("invalid flags: %x\n", submit_bo.flags);
 131                        ret = -EINVAL;
 132                        i = 0;
 133                        goto out;
 134                }
 135
 136                submit->bos[i].handle = submit_bo.handle;
 137                submit->bos[i].flags = submit_bo.flags;
 138                /* in validate_objects() we figure out if this is true: */
 139                submit->bos[i].iova  = submit_bo.presumed;
 140        }
 141
 142        spin_lock(&file->table_lock);
 143
 144        for (i = 0; i < args->nr_bos; i++) {
 145                struct drm_gem_object *obj;
 146
 147                /* normally use drm_gem_object_lookup(), but for bulk lookup
 148                 * all under single table_lock just hit object_idr directly:
 149                 */
 150                obj = idr_find(&file->object_idr, submit->bos[i].handle);
 151                if (!obj) {
 152                        DRM_ERROR("invalid handle %u at index %u\n", submit->bos[i].handle, i);
 153                        ret = -EINVAL;
 154                        goto out_unlock;
 155                }
 156
 157                drm_gem_object_get(obj);
 158
 159                submit->bos[i].obj = to_msm_bo(obj);
 160        }
 161
 162out_unlock:
 163        spin_unlock(&file->table_lock);
 164
 165out:
 166        submit->nr_bos = i;
 167
 168        return ret;
 169}
 170
 171static int submit_lookup_cmds(struct msm_gem_submit *submit,
 172                struct drm_msm_gem_submit *args, struct drm_file *file)
 173{
 174        unsigned i;
 175        size_t sz;
 176        int ret = 0;
 177
 178        for (i = 0; i < args->nr_cmds; i++) {
 179                struct drm_msm_gem_submit_cmd submit_cmd;
 180                void __user *userptr =
 181                        u64_to_user_ptr(args->cmds + (i * sizeof(submit_cmd)));
 182
 183                ret = copy_from_user(&submit_cmd, userptr, sizeof(submit_cmd));
 184                if (ret) {
 185                        ret = -EFAULT;
 186                        goto out;
 187                }
 188
 189                /* validate input from userspace: */
 190                switch (submit_cmd.type) {
 191                case MSM_SUBMIT_CMD_BUF:
 192                case MSM_SUBMIT_CMD_IB_TARGET_BUF:
 193                case MSM_SUBMIT_CMD_CTX_RESTORE_BUF:
 194                        break;
 195                default:
 196                        DRM_ERROR("invalid type: %08x\n", submit_cmd.type);
 197                        return -EINVAL;
 198                }
 199
 200                if (submit_cmd.size % 4) {
 201                        DRM_ERROR("non-aligned cmdstream buffer size: %u\n",
 202                                        submit_cmd.size);
 203                        ret = -EINVAL;
 204                        goto out;
 205                }
 206
 207                submit->cmd[i].type = submit_cmd.type;
 208                submit->cmd[i].size = submit_cmd.size / 4;
 209                submit->cmd[i].offset = submit_cmd.submit_offset / 4;
 210                submit->cmd[i].idx  = submit_cmd.submit_idx;
 211                submit->cmd[i].nr_relocs = submit_cmd.nr_relocs;
 212
 213                userptr = u64_to_user_ptr(submit_cmd.relocs);
 214
 215                sz = array_size(submit_cmd.nr_relocs,
 216                                sizeof(struct drm_msm_gem_submit_reloc));
 217                /* check for overflow: */
 218                if (sz == SIZE_MAX) {
 219                        ret = -ENOMEM;
 220                        goto out;
 221                }
 222                submit->cmd[i].relocs = kmalloc(sz, GFP_KERNEL);
 223                ret = copy_from_user(submit->cmd[i].relocs, userptr, sz);
 224                if (ret) {
 225                        ret = -EFAULT;
 226                        goto out;
 227                }
 228        }
 229
 230out:
 231        return ret;
 232}
 233
 234/* Unwind bo state, according to cleanup_flags.  In the success case, only
 235 * the lock is dropped at the end of the submit (and active/pin ref is dropped
 236 * later when the submit is retired).
 237 */
 238static void submit_cleanup_bo(struct msm_gem_submit *submit, int i,
 239                unsigned cleanup_flags)
 240{
 241        struct drm_gem_object *obj = &submit->bos[i].obj->base;
 242        unsigned flags = submit->bos[i].flags & cleanup_flags;
 243
 244        if (flags & BO_PINNED)
 245                msm_gem_unpin_iova_locked(obj, submit->aspace);
 246
 247        if (flags & BO_ACTIVE)
 248                msm_gem_active_put(obj);
 249
 250        if (flags & BO_LOCKED)
 251                dma_resv_unlock(obj->resv);
 252
 253        submit->bos[i].flags &= ~cleanup_flags;
 254}
 255
 256static void submit_unlock_unpin_bo(struct msm_gem_submit *submit, int i)
 257{
 258        submit_cleanup_bo(submit, i, BO_PINNED | BO_ACTIVE | BO_LOCKED);
 259
 260        if (!(submit->bos[i].flags & BO_VALID))
 261                submit->bos[i].iova = 0;
 262}
 263
 264/* This is where we make sure all the bo's are reserved and pin'd: */
 265static int submit_lock_objects(struct msm_gem_submit *submit)
 266{
 267        int contended, slow_locked = -1, i, ret = 0;
 268
 269retry:
 270        for (i = 0; i < submit->nr_bos; i++) {
 271                struct msm_gem_object *msm_obj = submit->bos[i].obj;
 272
 273                if (slow_locked == i)
 274                        slow_locked = -1;
 275
 276                contended = i;
 277
 278                if (!(submit->bos[i].flags & BO_LOCKED)) {
 279                        ret = dma_resv_lock_interruptible(msm_obj->base.resv,
 280                                                          &submit->ticket);
 281                        if (ret)
 282                                goto fail;
 283                        submit->bos[i].flags |= BO_LOCKED;
 284                }
 285        }
 286
 287        ww_acquire_done(&submit->ticket);
 288
 289        return 0;
 290
 291fail:
 292        if (ret == -EALREADY) {
 293                DRM_ERROR("handle %u at index %u already on submit list\n",
 294                                submit->bos[i].handle, i);
 295                ret = -EINVAL;
 296        }
 297
 298        for (; i >= 0; i--)
 299                submit_unlock_unpin_bo(submit, i);
 300
 301        if (slow_locked > 0)
 302                submit_unlock_unpin_bo(submit, slow_locked);
 303
 304        if (ret == -EDEADLK) {
 305                struct msm_gem_object *msm_obj = submit->bos[contended].obj;
 306                /* we lost out in a seqno race, lock and retry.. */
 307                ret = dma_resv_lock_slow_interruptible(msm_obj->base.resv,
 308                                                       &submit->ticket);
 309                if (!ret) {
 310                        submit->bos[contended].flags |= BO_LOCKED;
 311                        slow_locked = contended;
 312                        goto retry;
 313                }
 314
 315                /* Not expecting -EALREADY here, if the bo was already
 316                 * locked, we should have gotten -EALREADY already from
 317                 * the dma_resv_lock_interruptable() call.
 318                 */
 319                WARN_ON_ONCE(ret == -EALREADY);
 320        }
 321
 322        return ret;
 323}
 324
 325static int submit_fence_sync(struct msm_gem_submit *submit, bool no_implicit)
 326{
 327        int i, ret = 0;
 328
 329        for (i = 0; i < submit->nr_bos; i++) {
 330                struct drm_gem_object *obj = &submit->bos[i].obj->base;
 331                bool write = submit->bos[i].flags & MSM_SUBMIT_BO_WRITE;
 332
 333                if (!write) {
 334                        /* NOTE: _reserve_shared() must happen before
 335                         * _add_shared_fence(), which makes this a slightly
 336                         * strange place to call it.  OTOH this is a
 337                         * convenient can-fail point to hook it in.
 338                         */
 339                        ret = dma_resv_reserve_shared(obj->resv, 1);
 340                        if (ret)
 341                                return ret;
 342                }
 343
 344                if (no_implicit)
 345                        continue;
 346
 347                ret = drm_gem_fence_array_add_implicit(&submit->deps, obj,
 348                        write);
 349                if (ret)
 350                        break;
 351        }
 352
 353        return ret;
 354}
 355
 356static int submit_pin_objects(struct msm_gem_submit *submit)
 357{
 358        int i, ret = 0;
 359
 360        submit->valid = true;
 361
 362        /*
 363         * Increment active_count first, so if under memory pressure, we
 364         * don't inadvertently evict a bo needed by the submit in order
 365         * to pin an earlier bo in the same submit.
 366         */
 367        for (i = 0; i < submit->nr_bos; i++) {
 368                struct drm_gem_object *obj = &submit->bos[i].obj->base;
 369
 370                msm_gem_active_get(obj, submit->gpu);
 371                submit->bos[i].flags |= BO_ACTIVE;
 372        }
 373
 374        for (i = 0; i < submit->nr_bos; i++) {
 375                struct drm_gem_object *obj = &submit->bos[i].obj->base;
 376                uint64_t iova;
 377
 378                /* if locking succeeded, pin bo: */
 379                ret = msm_gem_get_and_pin_iova_locked(obj,
 380                                submit->aspace, &iova);
 381
 382                if (ret)
 383                        break;
 384
 385                submit->bos[i].flags |= BO_PINNED;
 386
 387                if (iova == submit->bos[i].iova) {
 388                        submit->bos[i].flags |= BO_VALID;
 389                } else {
 390                        submit->bos[i].iova = iova;
 391                        /* iova changed, so address in cmdstream is not valid: */
 392                        submit->bos[i].flags &= ~BO_VALID;
 393                        submit->valid = false;
 394                }
 395        }
 396
 397        return ret;
 398}
 399
 400static void submit_attach_object_fences(struct msm_gem_submit *submit)
 401{
 402        int i;
 403
 404        for (i = 0; i < submit->nr_bos; i++) {
 405                struct drm_gem_object *obj = &submit->bos[i].obj->base;
 406
 407                if (submit->bos[i].flags & MSM_SUBMIT_BO_WRITE)
 408                        dma_resv_add_excl_fence(obj->resv, submit->user_fence);
 409                else if (submit->bos[i].flags & MSM_SUBMIT_BO_READ)
 410                        dma_resv_add_shared_fence(obj->resv, submit->user_fence);
 411        }
 412}
 413
 414static int submit_bo(struct msm_gem_submit *submit, uint32_t idx,
 415                struct msm_gem_object **obj, uint64_t *iova, bool *valid)
 416{
 417        if (idx >= submit->nr_bos) {
 418                DRM_ERROR("invalid buffer index: %u (out of %u)\n",
 419                                idx, submit->nr_bos);
 420                return -EINVAL;
 421        }
 422
 423        if (obj)
 424                *obj = submit->bos[idx].obj;
 425        if (iova)
 426                *iova = submit->bos[idx].iova;
 427        if (valid)
 428                *valid = !!(submit->bos[idx].flags & BO_VALID);
 429
 430        return 0;
 431}
 432
 433/* process the reloc's and patch up the cmdstream as needed: */
 434static int submit_reloc(struct msm_gem_submit *submit, struct msm_gem_object *obj,
 435                uint32_t offset, uint32_t nr_relocs, struct drm_msm_gem_submit_reloc *relocs)
 436{
 437        uint32_t i, last_offset = 0;
 438        uint32_t *ptr;
 439        int ret = 0;
 440
 441        if (!nr_relocs)
 442                return 0;
 443
 444        if (offset % 4) {
 445                DRM_ERROR("non-aligned cmdstream buffer: %u\n", offset);
 446                return -EINVAL;
 447        }
 448
 449        /* For now, just map the entire thing.  Eventually we probably
 450         * to do it page-by-page, w/ kmap() if not vmap()d..
 451         */
 452        ptr = msm_gem_get_vaddr_locked(&obj->base);
 453
 454        if (IS_ERR(ptr)) {
 455                ret = PTR_ERR(ptr);
 456                DBG("failed to map: %d", ret);
 457                return ret;
 458        }
 459
 460        for (i = 0; i < nr_relocs; i++) {
 461                struct drm_msm_gem_submit_reloc submit_reloc = relocs[i];
 462                uint32_t off;
 463                uint64_t iova;
 464                bool valid;
 465
 466                if (submit_reloc.submit_offset % 4) {
 467                        DRM_ERROR("non-aligned reloc offset: %u\n",
 468                                        submit_reloc.submit_offset);
 469                        ret = -EINVAL;
 470                        goto out;
 471                }
 472
 473                /* offset in dwords: */
 474                off = submit_reloc.submit_offset / 4;
 475
 476                if ((off >= (obj->base.size / 4)) ||
 477                                (off < last_offset)) {
 478                        DRM_ERROR("invalid offset %u at reloc %u\n", off, i);
 479                        ret = -EINVAL;
 480                        goto out;
 481                }
 482
 483                ret = submit_bo(submit, submit_reloc.reloc_idx, NULL, &iova, &valid);
 484                if (ret)
 485                        goto out;
 486
 487                if (valid)
 488                        continue;
 489
 490                iova += submit_reloc.reloc_offset;
 491
 492                if (submit_reloc.shift < 0)
 493                        iova >>= -submit_reloc.shift;
 494                else
 495                        iova <<= submit_reloc.shift;
 496
 497                ptr[off] = iova | submit_reloc.or;
 498
 499                last_offset = off;
 500        }
 501
 502out:
 503        msm_gem_put_vaddr_locked(&obj->base);
 504
 505        return ret;
 506}
 507
 508/* Cleanup submit at end of ioctl.  In the error case, this also drops
 509 * references, unpins, and drops active refcnt.  In the non-error case,
 510 * this is done when the submit is retired.
 511 */
 512static void submit_cleanup(struct msm_gem_submit *submit, bool error)
 513{
 514        unsigned cleanup_flags = BO_LOCKED;
 515        unsigned i;
 516
 517        if (error)
 518                cleanup_flags |= BO_PINNED | BO_ACTIVE;
 519
 520        for (i = 0; i < submit->nr_bos; i++) {
 521                struct msm_gem_object *msm_obj = submit->bos[i].obj;
 522                submit_cleanup_bo(submit, i, cleanup_flags);
 523                if (error)
 524                        drm_gem_object_put(&msm_obj->base);
 525        }
 526}
 527
 528void msm_submit_retire(struct msm_gem_submit *submit)
 529{
 530        int i;
 531
 532        for (i = 0; i < submit->nr_bos; i++) {
 533                struct drm_gem_object *obj = &submit->bos[i].obj->base;
 534
 535                msm_gem_lock(obj);
 536                submit_cleanup_bo(submit, i, BO_PINNED | BO_ACTIVE);
 537                msm_gem_unlock(obj);
 538                drm_gem_object_put(obj);
 539        }
 540}
 541
 542struct msm_submit_post_dep {
 543        struct drm_syncobj *syncobj;
 544        uint64_t point;
 545        struct dma_fence_chain *chain;
 546};
 547
 548static struct drm_syncobj **msm_parse_deps(struct msm_gem_submit *submit,
 549                                           struct drm_file *file,
 550                                           uint64_t in_syncobjs_addr,
 551                                           uint32_t nr_in_syncobjs,
 552                                           size_t syncobj_stride,
 553                                           struct msm_ringbuffer *ring)
 554{
 555        struct drm_syncobj **syncobjs = NULL;
 556        struct drm_msm_gem_submit_syncobj syncobj_desc = {0};
 557        int ret = 0;
 558        uint32_t i, j;
 559
 560        syncobjs = kcalloc(nr_in_syncobjs, sizeof(*syncobjs),
 561                           GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY);
 562        if (!syncobjs)
 563                return ERR_PTR(-ENOMEM);
 564
 565        for (i = 0; i < nr_in_syncobjs; ++i) {
 566                uint64_t address = in_syncobjs_addr + i * syncobj_stride;
 567                struct dma_fence *fence;
 568
 569                if (copy_from_user(&syncobj_desc,
 570                                   u64_to_user_ptr(address),
 571                                   min(syncobj_stride, sizeof(syncobj_desc)))) {
 572                        ret = -EFAULT;
 573                        break;
 574                }
 575
 576                if (syncobj_desc.point &&
 577                    !drm_core_check_feature(submit->dev, DRIVER_SYNCOBJ_TIMELINE)) {
 578                        ret = -EOPNOTSUPP;
 579                        break;
 580                }
 581
 582                if (syncobj_desc.flags & ~MSM_SUBMIT_SYNCOBJ_FLAGS) {
 583                        ret = -EINVAL;
 584                        break;
 585                }
 586
 587                ret = drm_syncobj_find_fence(file, syncobj_desc.handle,
 588                                             syncobj_desc.point, 0, &fence);
 589                if (ret)
 590                        break;
 591
 592                ret = drm_gem_fence_array_add(&submit->deps, fence);
 593                if (ret)
 594                        break;
 595
 596                if (syncobj_desc.flags & MSM_SUBMIT_SYNCOBJ_RESET) {
 597                        syncobjs[i] =
 598                                drm_syncobj_find(file, syncobj_desc.handle);
 599                        if (!syncobjs[i]) {
 600                                ret = -EINVAL;
 601                                break;
 602                        }
 603                }
 604        }
 605
 606        if (ret) {
 607                for (j = 0; j <= i; ++j) {
 608                        if (syncobjs[j])
 609                                drm_syncobj_put(syncobjs[j]);
 610                }
 611                kfree(syncobjs);
 612                return ERR_PTR(ret);
 613        }
 614        return syncobjs;
 615}
 616
 617static void msm_reset_syncobjs(struct drm_syncobj **syncobjs,
 618                               uint32_t nr_syncobjs)
 619{
 620        uint32_t i;
 621
 622        for (i = 0; syncobjs && i < nr_syncobjs; ++i) {
 623                if (syncobjs[i])
 624                        drm_syncobj_replace_fence(syncobjs[i], NULL);
 625        }
 626}
 627
 628static struct msm_submit_post_dep *msm_parse_post_deps(struct drm_device *dev,
 629                                                       struct drm_file *file,
 630                                                       uint64_t syncobjs_addr,
 631                                                       uint32_t nr_syncobjs,
 632                                                       size_t syncobj_stride)
 633{
 634        struct msm_submit_post_dep *post_deps;
 635        struct drm_msm_gem_submit_syncobj syncobj_desc = {0};
 636        int ret = 0;
 637        uint32_t i, j;
 638
 639        post_deps = kmalloc_array(nr_syncobjs, sizeof(*post_deps),
 640                                  GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY);
 641        if (!post_deps)
 642                return ERR_PTR(-ENOMEM);
 643
 644        for (i = 0; i < nr_syncobjs; ++i) {
 645                uint64_t address = syncobjs_addr + i * syncobj_stride;
 646
 647                if (copy_from_user(&syncobj_desc,
 648                                   u64_to_user_ptr(address),
 649                                   min(syncobj_stride, sizeof(syncobj_desc)))) {
 650                        ret = -EFAULT;
 651                        break;
 652                }
 653
 654                post_deps[i].point = syncobj_desc.point;
 655                post_deps[i].chain = NULL;
 656
 657                if (syncobj_desc.flags) {
 658                        ret = -EINVAL;
 659                        break;
 660                }
 661
 662                if (syncobj_desc.point) {
 663                        if (!drm_core_check_feature(dev,
 664                                                    DRIVER_SYNCOBJ_TIMELINE)) {
 665                                ret = -EOPNOTSUPP;
 666                                break;
 667                        }
 668
 669                        post_deps[i].chain = dma_fence_chain_alloc();
 670                        if (!post_deps[i].chain) {
 671                                ret = -ENOMEM;
 672                                break;
 673                        }
 674                }
 675
 676                post_deps[i].syncobj =
 677                        drm_syncobj_find(file, syncobj_desc.handle);
 678                if (!post_deps[i].syncobj) {
 679                        ret = -EINVAL;
 680                        break;
 681                }
 682        }
 683
 684        if (ret) {
 685                for (j = 0; j <= i; ++j) {
 686                        dma_fence_chain_free(post_deps[j].chain);
 687                        if (post_deps[j].syncobj)
 688                                drm_syncobj_put(post_deps[j].syncobj);
 689                }
 690
 691                kfree(post_deps);
 692                return ERR_PTR(ret);
 693        }
 694
 695        return post_deps;
 696}
 697
 698static void msm_process_post_deps(struct msm_submit_post_dep *post_deps,
 699                                  uint32_t count, struct dma_fence *fence)
 700{
 701        uint32_t i;
 702
 703        for (i = 0; post_deps && i < count; ++i) {
 704                if (post_deps[i].chain) {
 705                        drm_syncobj_add_point(post_deps[i].syncobj,
 706                                              post_deps[i].chain,
 707                                              fence, post_deps[i].point);
 708                        post_deps[i].chain = NULL;
 709                } else {
 710                        drm_syncobj_replace_fence(post_deps[i].syncobj,
 711                                                  fence);
 712                }
 713        }
 714}
 715
 716int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
 717                struct drm_file *file)
 718{
 719        static atomic_t ident = ATOMIC_INIT(0);
 720        struct msm_drm_private *priv = dev->dev_private;
 721        struct drm_msm_gem_submit *args = data;
 722        struct msm_file_private *ctx = file->driver_priv;
 723        struct msm_gem_submit *submit = NULL;
 724        struct msm_gpu *gpu = priv->gpu;
 725        struct msm_gpu_submitqueue *queue;
 726        struct msm_ringbuffer *ring;
 727        struct msm_submit_post_dep *post_deps = NULL;
 728        struct drm_syncobj **syncobjs_to_reset = NULL;
 729        int out_fence_fd = -1;
 730        struct pid *pid = get_pid(task_pid(current));
 731        bool has_ww_ticket = false;
 732        unsigned i;
 733        int ret, submitid;
 734
 735        if (!gpu)
 736                return -ENXIO;
 737
 738        if (args->pad)
 739                return -EINVAL;
 740
 741        /* for now, we just have 3d pipe.. eventually this would need to
 742         * be more clever to dispatch to appropriate gpu module:
 743         */
 744        if (MSM_PIPE_ID(args->flags) != MSM_PIPE_3D0)
 745                return -EINVAL;
 746
 747        if (MSM_PIPE_FLAGS(args->flags) & ~MSM_SUBMIT_FLAGS)
 748                return -EINVAL;
 749
 750        if (args->flags & MSM_SUBMIT_SUDO) {
 751                if (!IS_ENABLED(CONFIG_DRM_MSM_GPU_SUDO) ||
 752                    !capable(CAP_SYS_RAWIO))
 753                        return -EINVAL;
 754        }
 755
 756        queue = msm_submitqueue_get(ctx, args->queueid);
 757        if (!queue)
 758                return -ENOENT;
 759
 760        /* Get a unique identifier for the submission for logging purposes */
 761        submitid = atomic_inc_return(&ident) - 1;
 762
 763        ring = gpu->rb[queue->ring_nr];
 764        trace_msm_gpu_submit(pid_nr(pid), ring->id, submitid,
 765                args->nr_bos, args->nr_cmds);
 766
 767        ret = mutex_lock_interruptible(&queue->lock);
 768        if (ret)
 769                goto out_post_unlock;
 770
 771        if (args->flags & MSM_SUBMIT_FENCE_FD_OUT) {
 772                out_fence_fd = get_unused_fd_flags(O_CLOEXEC);
 773                if (out_fence_fd < 0) {
 774                        ret = out_fence_fd;
 775                        goto out_unlock;
 776                }
 777        }
 778
 779        submit = submit_create(dev, gpu, queue, args->nr_bos,
 780                args->nr_cmds);
 781        if (IS_ERR(submit)) {
 782                ret = PTR_ERR(submit);
 783                goto out_unlock;
 784        }
 785
 786        submit->pid = pid;
 787        submit->ident = submitid;
 788
 789        if (args->flags & MSM_SUBMIT_SUDO)
 790                submit->in_rb = true;
 791
 792        if (args->flags & MSM_SUBMIT_FENCE_FD_IN) {
 793                struct dma_fence *in_fence;
 794
 795                in_fence = sync_file_get_fence(args->fence_fd);
 796
 797                if (!in_fence) {
 798                        ret = -EINVAL;
 799                        goto out_unlock;
 800                }
 801
 802                ret = drm_gem_fence_array_add(&submit->deps, in_fence);
 803                if (ret)
 804                        goto out_unlock;
 805        }
 806
 807        if (args->flags & MSM_SUBMIT_SYNCOBJ_IN) {
 808                syncobjs_to_reset = msm_parse_deps(submit, file,
 809                                                   args->in_syncobjs,
 810                                                   args->nr_in_syncobjs,
 811                                                   args->syncobj_stride, ring);
 812                if (IS_ERR(syncobjs_to_reset)) {
 813                        ret = PTR_ERR(syncobjs_to_reset);
 814                        goto out_unlock;
 815                }
 816        }
 817
 818        if (args->flags & MSM_SUBMIT_SYNCOBJ_OUT) {
 819                post_deps = msm_parse_post_deps(dev, file,
 820                                                args->out_syncobjs,
 821                                                args->nr_out_syncobjs,
 822                                                args->syncobj_stride);
 823                if (IS_ERR(post_deps)) {
 824                        ret = PTR_ERR(post_deps);
 825                        goto out_unlock;
 826                }
 827        }
 828
 829        ret = submit_lookup_objects(submit, args, file);
 830        if (ret)
 831                goto out;
 832
 833        ret = submit_lookup_cmds(submit, args, file);
 834        if (ret)
 835                goto out;
 836
 837        /* copy_*_user while holding a ww ticket upsets lockdep */
 838        ww_acquire_init(&submit->ticket, &reservation_ww_class);
 839        has_ww_ticket = true;
 840        ret = submit_lock_objects(submit);
 841        if (ret)
 842                goto out;
 843
 844        ret = submit_fence_sync(submit, !!(args->flags & MSM_SUBMIT_NO_IMPLICIT));
 845        if (ret)
 846                goto out;
 847
 848        ret = submit_pin_objects(submit);
 849        if (ret)
 850                goto out;
 851
 852        for (i = 0; i < args->nr_cmds; i++) {
 853                struct msm_gem_object *msm_obj;
 854                uint64_t iova;
 855
 856                ret = submit_bo(submit, submit->cmd[i].idx,
 857                                &msm_obj, &iova, NULL);
 858                if (ret)
 859                        goto out;
 860
 861                if (!submit->cmd[i].size ||
 862                        ((submit->cmd[i].size + submit->cmd[i].offset) >
 863                                msm_obj->base.size / 4)) {
 864                        DRM_ERROR("invalid cmdstream size: %u\n", submit->cmd[i].size * 4);
 865                        ret = -EINVAL;
 866                        goto out;
 867                }
 868
 869                submit->cmd[i].iova = iova + (submit->cmd[i].offset * 4);
 870
 871                if (submit->valid)
 872                        continue;
 873
 874                ret = submit_reloc(submit, msm_obj, submit->cmd[i].offset * 4,
 875                                submit->cmd[i].nr_relocs, submit->cmd[i].relocs);
 876                if (ret)
 877                        goto out;
 878        }
 879
 880        submit->nr_cmds = i;
 881
 882        submit->user_fence = dma_fence_get(&submit->base.s_fence->finished);
 883
 884        /*
 885         * Allocate an id which can be used by WAIT_FENCE ioctl to map back
 886         * to the underlying fence.
 887         */
 888        submit->fence_id = idr_alloc_cyclic(&queue->fence_idr,
 889                        submit->user_fence, 0, INT_MAX, GFP_KERNEL);
 890        if (submit->fence_id < 0) {
 891                ret = submit->fence_id = 0;
 892                submit->fence_id = 0;
 893                goto out;
 894        }
 895
 896        if (args->flags & MSM_SUBMIT_FENCE_FD_OUT) {
 897                struct sync_file *sync_file = sync_file_create(submit->user_fence);
 898                if (!sync_file) {
 899                        ret = -ENOMEM;
 900                        goto out;
 901                }
 902                fd_install(out_fence_fd, sync_file->file);
 903                args->fence_fd = out_fence_fd;
 904        }
 905
 906        submit_attach_object_fences(submit);
 907
 908        /* The scheduler owns a ref now: */
 909        msm_gem_submit_get(submit);
 910
 911        drm_sched_entity_push_job(&submit->base, queue->entity);
 912
 913        args->fence = submit->fence_id;
 914
 915        msm_reset_syncobjs(syncobjs_to_reset, args->nr_in_syncobjs);
 916        msm_process_post_deps(post_deps, args->nr_out_syncobjs,
 917                              submit->user_fence);
 918
 919
 920out:
 921        submit_cleanup(submit, !!ret);
 922        if (has_ww_ticket)
 923                ww_acquire_fini(&submit->ticket);
 924out_unlock:
 925        if (ret && (out_fence_fd >= 0))
 926                put_unused_fd(out_fence_fd);
 927        mutex_unlock(&queue->lock);
 928        if (submit)
 929                msm_gem_submit_put(submit);
 930out_post_unlock:
 931        if (!IS_ERR_OR_NULL(post_deps)) {
 932                for (i = 0; i < args->nr_out_syncobjs; ++i) {
 933                        kfree(post_deps[i].chain);
 934                        drm_syncobj_put(post_deps[i].syncobj);
 935                }
 936                kfree(post_deps);
 937        }
 938
 939        if (!IS_ERR_OR_NULL(syncobjs_to_reset)) {
 940                for (i = 0; i < args->nr_in_syncobjs; ++i) {
 941                        if (syncobjs_to_reset[i])
 942                                drm_syncobj_put(syncobjs_to_reset[i]);
 943                }
 944                kfree(syncobjs_to_reset);
 945        }
 946
 947        return ret;
 948}
 949