unsigned int pinctl;
        u32 alignment;
 
-       WARN_ON(!mutex_is_locked(&dev->struct_mutex));
        if (WARN_ON(!i915_gem_object_is_framebuffer(obj)))
                return ERR_PTR(-EINVAL);
 
 
 void intel_unpin_fb_vma(struct i915_vma *vma, unsigned long flags)
 {
-       lockdep_assert_held(&vma->vm->i915->drm.struct_mutex);
-
        i915_gem_object_lock(vma->obj);
        if (flags & PLANE_HAS_FENCE)
                i915_vma_unpin_fence(vma);
                return false;
        }
 
-       mutex_lock(&dev->struct_mutex);
        obj = i915_gem_object_create_stolen_for_preallocated(dev_priv,
                                                             base_aligned,
                                                             base_aligned,
                                                             size_aligned);
-       mutex_unlock(&dev->struct_mutex);
        if (!obj)
                return false;
 
        intel_state->color_plane[0].stride =
                intel_fb_pitch(fb, 0, intel_state->base.rotation);
 
-       mutex_lock(&dev->struct_mutex);
        intel_state->vma =
                intel_pin_and_fence_fb_obj(fb,
                                           &intel_state->view,
                                           intel_plane_uses_fence(intel_state),
                                           &intel_state->flags);
-       mutex_unlock(&dev->struct_mutex);
        if (IS_ERR(intel_state->vma)) {
                DRM_ERROR("failed to pin boot fb on pipe %d: %li\n",
                          intel_crtc->pipe, PTR_ERR(intel_state->vma));
  * bits.  Some older platforms need special physical address handling for
  * cursor planes.
  *
- * Must be called with struct_mutex held.
- *
  * Returns 0 on success, negative error code on failure.
  */
 int
        if (ret)
                return ret;
 
-       ret = mutex_lock_interruptible(&dev_priv->drm.struct_mutex);
-       if (ret) {
-               i915_gem_object_unpin_pages(obj);
-               return ret;
-       }
-
        ret = intel_plane_pin_fb(to_intel_plane_state(new_state));
 
-       mutex_unlock(&dev_priv->drm.struct_mutex);
        i915_gem_object_unpin_pages(obj);
        if (ret)
                return ret;
  * @old_state: the state from the previous modeset
  *
  * Cleans up a framebuffer that has just been removed from a plane.
- *
- * Must be called with struct_mutex held.
  */
 void
 intel_cleanup_plane_fb(struct drm_plane *plane,
        }
 
        /* Should only be called after a successful intel_prepare_plane_fb()! */
-       mutex_lock(&dev_priv->drm.struct_mutex);
        intel_plane_unpin_fb(to_intel_plane_state(old_state));
-       mutex_unlock(&dev_priv->drm.struct_mutex);
 }
 
 int
                           u32 src_w, u32 src_h,
                           struct drm_modeset_acquire_ctx *ctx)
 {
-       struct drm_i915_private *dev_priv = to_i915(crtc->dev);
        struct drm_plane_state *old_plane_state, *new_plane_state;
        struct intel_plane *intel_plane = to_intel_plane(plane);
        struct intel_crtc_state *crtc_state =
        if (ret)
                goto out_free;
 
-       ret = mutex_lock_interruptible(&dev_priv->drm.struct_mutex);
-       if (ret)
-               goto out_free;
-
        ret = intel_plane_pin_fb(to_intel_plane_state(new_plane_state));
        if (ret)
-               goto out_unlock;
+               goto out_free;
 
        intel_frontbuffer_flush(to_intel_frontbuffer(fb), ORIGIN_FLIP);
        intel_frontbuffer_track(to_intel_frontbuffer(old_plane_state->fb),
 
        intel_plane_unpin_fb(to_intel_plane_state(old_plane_state));
 
-out_unlock:
-       mutex_unlock(&dev_priv->drm.struct_mutex);
 out_free:
        if (new_crtc_state)
                intel_crtc_destroy_state(crtc, &new_crtc_state->base);
 
                goto err;
        }
 
-       mutex_lock(&i915->drm.struct_mutex);
        vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0, PIN_MAPPABLE);
-       mutex_unlock(&i915->drm.struct_mutex);
        if (IS_ERR(vma)) {
                DRM_ERROR("Vma creation failed\n");
                i915_gem_object_put(obj);
                return;
 
        if (atomic_dec_and_test(&dsb->refcount)) {
-               mutex_lock(&i915->drm.struct_mutex);
-               i915_gem_object_unpin_map(dsb->vma->obj);
-               i915_vma_unpin_and_release(&dsb->vma, 0);
-               mutex_unlock(&i915->drm.struct_mutex);
+               i915_vma_unpin_and_release(&dsb->vma, I915_VMA_RELEASE_MAP);
                dsb->cmd_buf = NULL;
                dsb->free_pos = 0;
                dsb->ins_start_offset = 0;
 
                sizes->fb_height = intel_fb->base.height;
        }
 
-       mutex_lock(&dev->struct_mutex);
        wakeref = intel_runtime_pm_get(&dev_priv->runtime_pm);
 
        /* Pin the GGTT vma for our access via info->screen_base.
        ifbdev->vma_flags = flags;
 
        intel_runtime_pm_put(&dev_priv->runtime_pm, wakeref);
-       mutex_unlock(&dev->struct_mutex);
        vga_switcheroo_client_fb_set(pdev, info);
        return 0;
 
        intel_unpin_fb_vma(vma, flags);
 out_unlock:
        intel_runtime_pm_put(&dev_priv->runtime_pm, wakeref);
-       mutex_unlock(&dev->struct_mutex);
        return ret;
 }
 
 
        drm_fb_helper_fini(&ifbdev->helper);
 
-       if (ifbdev->vma) {
-               mutex_lock(&ifbdev->helper.dev->struct_mutex);
+       if (ifbdev->vma)
                intel_unpin_fb_vma(ifbdev->vma, ifbdev->vma_flags);
-               mutex_unlock(&ifbdev->helper.dev->struct_mutex);
-       }
 
        if (ifbdev->fb)
                drm_framebuffer_remove(&ifbdev->fb->base);
 
        struct i915_vma *vma;
        int err;
 
-       mutex_lock(&i915->drm.struct_mutex);
-
        obj = i915_gem_object_create_stolen(i915, PAGE_SIZE);
        if (obj == NULL)
                obj = i915_gem_object_create_internal(i915, PAGE_SIZE);
-       if (IS_ERR(obj)) {
-               err = PTR_ERR(obj);
-               goto err_unlock;
-       }
+       if (IS_ERR(obj))
+               return PTR_ERR(obj);
 
        vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0, PIN_MAPPABLE);
        if (IS_ERR(vma)) {
        }
 
        overlay->reg_bo = obj;
-       mutex_unlock(&i915->drm.struct_mutex);
        return 0;
 
 err_put_bo:
        i915_gem_object_put(obj);
-err_unlock:
-       mutex_unlock(&i915->drm.struct_mutex);
        return err;
 }
 
 
         * keep track of the GPU activity within this vma/request, and
         * propagate the signal from the request to w->dma.
         */
-       err = i915_active_add_request(&vma->active, rq);
+       err = __i915_vma_move_to_active(vma, rq);
        if (err)
                goto out_request;
 
 
        GEM_BUG_ON(!i915_gem_context_is_closed(ctx));
 
        release_hw_id(ctx);
-       if (ctx->vm)
-               i915_vm_put(ctx->vm);
 
        free_engines(rcu_access_pointer(ctx->engines));
        mutex_destroy(&ctx->engines_mutex);
 
 static void context_close(struct i915_gem_context *ctx)
 {
+       i915_gem_context_set_closed(ctx);
+
+       if (ctx->vm)
+               i915_vm_close(ctx->vm);
+
        mutex_lock(&ctx->mutex);
 
-       i915_gem_context_set_closed(ctx);
        ctx->file_priv = ERR_PTR(-EBADF);
 
        /*
 
        GEM_BUG_ON(old && i915_vm_is_4lvl(vm) != i915_vm_is_4lvl(old));
 
-       ctx->vm = i915_vm_get(vm);
+       ctx->vm = i915_vm_open(vm);
        context_apply_all(ctx, __apply_ppgtt, vm);
 
        return old;
 
        vm = __set_ppgtt(ctx, vm);
        if (vm)
-               i915_vm_put(vm);
+               i915_vm_close(vm);
 }
 
 static void __set_timeline(struct intel_timeline **dst,
        if (ret < 0)
                goto err_unlock;
 
-       i915_vm_get(vm);
+       i915_vm_open(vm);
 
        args->size = 0;
        args->value = ret;
        if (INTEL_GEN(old->i915) < 8)
                gen6_ppgtt_unpin_all(i915_vm_to_ppgtt(old));
 
-       i915_vm_put(old);
+       i915_vm_close(old);
 }
 
 static int emit_ppgtt_update(struct i915_request *rq, void *data)
                                   set_ppgtt_barrier,
                                   old);
        if (err) {
-               i915_vm_put(__set_ppgtt(ctx, old));
-               i915_vm_put(old);
+               i915_vm_close(__set_ppgtt(ctx, old));
+               i915_vm_close(old);
        }
 
 unlock:
 
                        if (!drm_mm_node_allocated(&vma->node))
                                continue;
 
-                       ret = i915_vma_bind(vma, cache_level, PIN_UPDATE);
+                       /* Wait for an earlier async bind, need to rewrite it */
+                       ret = i915_vma_sync(vma);
+                       if (ret)
+                               return ret;
+
+                       ret = i915_vma_bind(vma, cache_level, PIN_UPDATE, NULL);
                        if (ret)
                                return ret;
                }
        if (ret)
                goto out;
 
-       ret = mutex_lock_interruptible(&i915->drm.struct_mutex);
-       if (ret)
-               goto out;
-
        ret = i915_gem_object_lock_interruptible(obj);
        if (ret == 0) {
                ret = i915_gem_object_set_cache_level(obj, level);
                i915_gem_object_unlock(obj);
        }
-       mutex_unlock(&i915->drm.struct_mutex);
 
 out:
        i915_gem_object_put(obj);
                if (!drm_mm_node_allocated(&vma->node))
                        continue;
 
+               GEM_BUG_ON(vma->vm != &i915->ggtt.vm);
                list_move_tail(&vma->vm_link, &vma->vm->bound_list);
        }
        mutex_unlock(&i915->ggtt.vm.mutex);
 
 
                case 1:
                        /* Too fragmented, unbind everything and retry */
+                       mutex_lock(&eb->context->vm->mutex);
                        err = i915_gem_evict_vm(eb->context->vm);
+                       mutex_unlock(&eb->context->vm->mutex);
                        if (err)
                                return err;
                        break;
                        ggtt->vm.clear_range(&ggtt->vm,
                                             cache->node.start,
                                             cache->node.size);
+                       mutex_lock(&ggtt->vm.mutex);
                        drm_mm_remove_node(&cache->node);
+                       mutex_unlock(&ggtt->vm.mutex);
                } else {
                        i915_vma_unpin((struct i915_vma *)cache->node.mm);
                }
                                               PIN_NOEVICT);
                if (IS_ERR(vma)) {
                        memset(&cache->node, 0, sizeof(cache->node));
+                       mutex_lock(&ggtt->vm.mutex);
                        err = drm_mm_insert_node_in_range
                                (&ggtt->vm.mm, &cache->node,
                                 PAGE_SIZE, 0, I915_COLOR_UNEVICTABLE,
                                 0, ggtt->mappable_end,
                                 DRM_MM_INSERT_LOW);
+                       mutex_unlock(&ggtt->vm.mutex);
                        if (err) /* no inactive aperture space, use cpu reloc */
                                return NULL;
                } else {
                if (reloc->write_domain == I915_GEM_DOMAIN_INSTRUCTION &&
                    IS_GEN(eb->i915, 6)) {
                        err = i915_vma_bind(target, target->obj->cache_level,
-                                           PIN_GLOBAL);
+                                           PIN_GLOBAL, NULL);
                        if (WARN_ONCE(err,
                                      "Unexpected failure to bind target VMA!"))
                                return err;
        return i915_request_get(rq);
 }
 
-static int
-__eb_pin_context(struct i915_execbuffer *eb, struct intel_context *ce)
-{
-       int err;
-
-       if (likely(atomic_inc_not_zero(&ce->pin_count)))
-               return 0;
-
-       err = mutex_lock_interruptible(&eb->i915->drm.struct_mutex);
-       if (err)
-               return err;
-
-       err = __intel_context_do_pin(ce);
-       mutex_unlock(&eb->i915->drm.struct_mutex);
-
-       return err;
-}
-
-static void
-__eb_unpin_context(struct i915_execbuffer *eb, struct intel_context *ce)
-{
-       if (likely(atomic_add_unless(&ce->pin_count, -1, 1)))
-               return;
-
-       mutex_lock(&eb->i915->drm.struct_mutex);
-       intel_context_unpin(ce);
-       mutex_unlock(&eb->i915->drm.struct_mutex);
-}
-
 static int __eb_pin_engine(struct i915_execbuffer *eb, struct intel_context *ce)
 {
        struct intel_timeline *tl;
         * GGTT space, so do this first before we reserve a seqno for
         * ourselves.
         */
-       err = __eb_pin_context(eb, ce);
+       err = intel_context_pin(ce);
        if (err)
                return err;
 
        intel_context_exit(ce);
        intel_context_timeline_unlock(tl);
 err_unpin:
-       __eb_unpin_context(eb, ce);
+       intel_context_unpin(ce);
        return err;
 }
 
        intel_context_exit(ce);
        mutex_unlock(&tl->mutex);
 
-       __eb_unpin_context(eb, ce);
+       intel_context_unpin(ce);
 }
 
 static unsigned int
 
        if (ret)
                goto err_rpm;
 
-       ret = i915_mutex_lock_interruptible(dev);
-       if (ret)
-               goto err_reset;
-
-       /* Access to snoopable pages through the GTT is incoherent. */
-       if (obj->cache_level != I915_CACHE_NONE && !HAS_LLC(i915)) {
-               ret = -EFAULT;
-               goto err_unlock;
-       }
-
        /* Now pin it into the GTT as needed */
        vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0,
                                       PIN_MAPPABLE |
        }
        if (IS_ERR(vma)) {
                ret = PTR_ERR(vma);
-               goto err_unlock;
+               goto err_reset;
+       }
+
+       /* Access to snoopable pages through the GTT is incoherent. */
+       if (obj->cache_level != I915_CACHE_NONE && !HAS_LLC(i915)) {
+               ret = -EFAULT;
+               goto err_unpin;
        }
 
        ret = i915_vma_pin_fence(vma);
        i915_vma_unpin_fence(vma);
 err_unpin:
        __i915_vma_unpin(vma);
-err_unlock:
-       mutex_unlock(&dev->struct_mutex);
 err_reset:
        intel_gt_reset_unlock(ggtt->vm.gt, srcu);
 err_rpm:
 
 
        wakeref = intel_runtime_pm_get(&i915->runtime_pm);
        llist_for_each_entry_safe(obj, on, freed, freed) {
-               struct i915_vma *vma, *vn;
-
                trace_i915_gem_object_destroy(obj);
 
-               mutex_lock(&i915->drm.struct_mutex);
-
-               list_for_each_entry_safe(vma, vn, &obj->vma.list, obj_link) {
-                       GEM_BUG_ON(i915_vma_is_active(vma));
-                       atomic_and(~I915_VMA_PIN_MASK, &vma->flags);
-                       i915_vma_destroy(vma);
+               if (!list_empty(&obj->vma.list)) {
+                       struct i915_vma *vma;
+
+                       /*
+                        * Note that the vma keeps an object reference while
+                        * it is active, so it *should* not sleep while we
+                        * destroy it. Our debug code errs insits it *might*.
+                        * For the moment, play along.
+                        */
+                       spin_lock(&obj->vma.lock);
+                       while ((vma = list_first_entry_or_null(&obj->vma.list,
+                                                              struct i915_vma,
+                                                              obj_link))) {
+                               GEM_BUG_ON(vma->obj != obj);
+                               spin_unlock(&obj->vma.lock);
+
+                               i915_vma_destroy(vma);
+
+                               spin_lock(&obj->vma.lock);
+                       }
+                       spin_unlock(&obj->vma.lock);
                }
-               GEM_BUG_ON(!list_empty(&obj->vma.list));
-               GEM_BUG_ON(!RB_EMPTY_ROOT(&obj->vma.tree));
-
-               mutex_unlock(&i915->drm.struct_mutex);
 
                GEM_BUG_ON(atomic_read(&obj->bind_count));
                GEM_BUG_ON(obj->userfault_count);
 
        dma_resv_lock(obj->base.resv, NULL);
 }
 
+static inline bool i915_gem_object_trylock(struct drm_i915_gem_object *obj)
+{
+       return dma_resv_trylock(obj->base.resv);
+}
+
 static inline int
 i915_gem_object_lock_interruptible(struct drm_i915_gem_object *obj)
 {
 
 
 #include "i915_trace.h"
 
-static bool shrinker_lock(struct drm_i915_private *i915,
-                         unsigned int flags,
-                         bool *unlock)
-{
-       struct mutex *m = &i915->drm.struct_mutex;
-
-       switch (mutex_trylock_recursive(m)) {
-       case MUTEX_TRYLOCK_RECURSIVE:
-               *unlock = false;
-               return true;
-
-       case MUTEX_TRYLOCK_FAILED:
-               *unlock = false;
-               if (flags & I915_SHRINK_ACTIVE &&
-                   mutex_lock_killable_nested(m, I915_MM_SHRINKER) == 0)
-                       *unlock = true;
-               return *unlock;
-
-       case MUTEX_TRYLOCK_SUCCESS:
-               *unlock = true;
-               return true;
-       }
-
-       BUG();
-}
-
-static void shrinker_unlock(struct drm_i915_private *i915, bool unlock)
-{
-       if (!unlock)
-               return;
-
-       mutex_unlock(&i915->drm.struct_mutex);
-}
-
 static bool swap_available(void)
 {
        return get_nr_swap_pages() > 0;
        intel_wakeref_t wakeref = 0;
        unsigned long count = 0;
        unsigned long scanned = 0;
-       bool unlock;
-
-       if (!shrinker_lock(i915, shrink, &unlock))
-               return 0;
 
        /*
         * When shrinking the active list, we should also consider active
        if (shrink & I915_SHRINK_BOUND)
                intel_runtime_pm_put(&i915->runtime_pm, wakeref);
 
-       shrinker_unlock(i915, unlock);
-
        if (nr_scanned)
                *nr_scanned += scanned;
        return count;
        struct drm_i915_private *i915 =
                container_of(shrinker, struct drm_i915_private, mm.shrinker);
        unsigned long freed;
-       bool unlock;
 
        sc->nr_scanned = 0;
 
-       if (!shrinker_lock(i915, 0, &unlock))
-               return SHRINK_STOP;
-
        freed = i915_gem_shrink(i915,
                                sc->nr_to_scan,
                                &sc->nr_scanned,
                                I915_SHRINK_BOUND |
-                               I915_SHRINK_UNBOUND |
-                               I915_SHRINK_WRITEBACK);
+                               I915_SHRINK_UNBOUND);
        if (sc->nr_scanned < sc->nr_to_scan && current_is_kswapd()) {
                intel_wakeref_t wakeref;
 
                }
        }
 
-       shrinker_unlock(i915, unlock);
-
        return sc->nr_scanned ? freed : SHRINK_STOP;
 }
 
        freed_pages = 0;
        with_intel_runtime_pm(&i915->runtime_pm, wakeref)
                freed_pages += i915_gem_shrink(i915, -1UL, NULL,
+                                              I915_SHRINK_ACTIVE |
                                               I915_SHRINK_BOUND |
                                               I915_SHRINK_UNBOUND |
                                               I915_SHRINK_WRITEBACK);
        struct i915_vma *vma, *next;
        unsigned long freed_pages = 0;
        intel_wakeref_t wakeref;
-       bool unlock;
-
-       if (!shrinker_lock(i915, 0, &unlock))
-               return NOTIFY_DONE;
 
        with_intel_runtime_pm(&i915->runtime_pm, wakeref)
                freed_pages += i915_gem_shrink(i915, -1UL, NULL,
                if (!vma->iomap || i915_vma_is_active(vma))
                        continue;
 
-               mutex_unlock(&i915->ggtt.vm.mutex);
-               if (i915_vma_unbind(vma) == 0)
+               if (__i915_vma_unbind(vma) == 0)
                        freed_pages += count;
-               mutex_lock(&i915->ggtt.vm.mutex);
        }
        mutex_unlock(&i915->ggtt.vm.mutex);
 
-       shrinker_unlock(i915, unlock);
-
        *(unsigned long *)ptr += freed_pages;
        return NOTIFY_DONE;
 }
 
        fs_reclaim_acquire(GFP_KERNEL);
 
-       /*
-        * As we invariably rely on the struct_mutex within the shrinker,
-        * but have a complicated recursion dance, taint all the mutexes used
-        * within the shrinker with the struct_mutex. For completeness, we
-        * taint with all subclass of struct_mutex, even though we should
-        * only need tainting by I915_MM_NORMAL to catch possible ABBA
-        * deadlocks from using struct_mutex inside @mutex.
-        */
-       mutex_acquire(&i915->drm.struct_mutex.dep_map,
-                     I915_MM_SHRINKER, 0, _RET_IP_);
-
        mutex_acquire(&mutex->dep_map, 0, 0, _RET_IP_);
        mutex_release(&mutex->dep_map, 0, _RET_IP_);
 
-       mutex_release(&i915->drm.struct_mutex.dep_map, 0, _RET_IP_);
-
        fs_reclaim_release(GFP_KERNEL);
 
        if (unlock)
 
        if (!drm_mm_initialized(&dev_priv->mm.stolen))
                return NULL;
 
-       lockdep_assert_held(&dev_priv->drm.struct_mutex);
-
        DRM_DEBUG_DRIVER("creating preallocated stolen object: stolen_offset=%pa, gtt_offset=%pa, size=%pa\n",
                         &stolen_offset, >t_offset, &size);
 
         * setting up the GTT space. The actual reservation will occur
         * later.
         */
+       mutex_lock(&ggtt->vm.mutex);
        ret = i915_gem_gtt_reserve(&ggtt->vm, &vma->node,
                                   size, gtt_offset, obj->cache_level,
                                   0);
        if (ret) {
                DRM_DEBUG_DRIVER("failed to allocate stolen GTT space\n");
+               mutex_unlock(&ggtt->vm.mutex);
                goto err_pages;
        }
 
        GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
 
+       GEM_BUG_ON(vma->pages);
        vma->pages = obj->mm.pages;
+       atomic_set(&vma->pages_count, I915_VMA_PAGES_ACTIVE);
+
        set_bit(I915_VMA_GLOBAL_BIND_BIT, __i915_vma_flags(vma));
        __i915_vma_set_map_and_fenceable(vma);
 
-       mutex_lock(&ggtt->vm.mutex);
        list_add_tail(&vma->vm_link, &ggtt->vm.bound_list);
        mutex_unlock(&ggtt->vm.mutex);
 
 
 i915_gem_object_fence_prepare(struct drm_i915_gem_object *obj,
                              int tiling_mode, unsigned int stride)
 {
+       struct i915_ggtt *ggtt = &to_i915(obj->base.dev)->ggtt;
        struct i915_vma *vma;
-       int ret;
+       int ret = 0;
 
        if (tiling_mode == I915_TILING_NONE)
                return 0;
 
+       mutex_lock(&ggtt->vm.mutex);
        for_each_ggtt_vma(vma, obj) {
                if (i915_vma_fence_prepare(vma, tiling_mode, stride))
                        continue;
 
-               ret = i915_vma_unbind(vma);
+               ret = __i915_vma_unbind(vma);
                if (ret)
-                       return ret;
+                       break;
        }
+       mutex_unlock(&ggtt->vm.mutex);
 
-       return 0;
+       return ret;
 }
 
 int
 
        GEM_BUG_ON(!i915_tiling_ok(obj, tiling, stride));
        GEM_BUG_ON(!stride ^ (tiling == I915_TILING_NONE));
-       lockdep_assert_held(&i915->drm.struct_mutex);
 
        if ((tiling | stride) == obj->tiling_and_stride)
                return 0;
         * whilst executing a fenced command for an untiled object.
         */
 
-       err = i915_gem_object_fence_prepare(obj, tiling, stride);
-       if (err)
-               return err;
-
        i915_gem_object_lock(obj);
        if (i915_gem_object_is_framebuffer(obj)) {
                i915_gem_object_unlock(obj);
                return -EBUSY;
        }
 
+       err = i915_gem_object_fence_prepare(obj, tiling, stride);
+       if (err) {
+               i915_gem_object_unlock(obj);
+               return err;
+       }
+
        /* If the memory has unknown (i.e. varying) swizzling, we pin the
         * pages to prevent them being swapped out and causing corruption
         * due to the change in swizzling.
                }
        }
 
-       err = mutex_lock_interruptible(&dev->struct_mutex);
-       if (err)
-               goto err;
-
        err = i915_gem_object_set_tiling(obj, args->tiling_mode, args->stride);
-       mutex_unlock(&dev->struct_mutex);
 
        /* We have to maintain this existing ABI... */
        args->stride = i915_gem_object_get_stride(obj);
 
        struct i915_mmu_notifier *mn =
                container_of(_mn, struct i915_mmu_notifier, mn);
        struct interval_tree_node *it;
-       struct mutex *unlock = NULL;
        unsigned long end;
        int ret = 0;
 
                }
                spin_unlock(&mn->lock);
 
-               if (!unlock) {
-                       unlock = &mn->mm->i915->drm.struct_mutex;
-
-                       switch (mutex_trylock_recursive(unlock)) {
-                       default:
-                       case MUTEX_TRYLOCK_FAILED:
-                               if (mutex_lock_killable_nested(unlock, I915_MM_SHRINKER)) {
-                                       i915_gem_object_put(obj);
-                                       return -EINTR;
-                               }
-                               /* fall through */
-                       case MUTEX_TRYLOCK_SUCCESS:
-                               break;
-
-                       case MUTEX_TRYLOCK_RECURSIVE:
-                               unlock = ERR_PTR(-EEXIST);
-                               break;
-                       }
-               }
-
                ret = i915_gem_object_unbind(obj,
                                             I915_GEM_OBJECT_UNBIND_ACTIVE);
                if (ret == 0)
                        ret = __i915_gem_object_put_pages(obj, I915_MM_SHRINKER);
                i915_gem_object_put(obj);
                if (ret)
-                       goto unlock;
+                       return ret;
 
                spin_lock(&mn->lock);
 
        }
        spin_unlock(&mn->lock);
 
-unlock:
-       if (!IS_ERR_OR_NULL(unlock))
-               mutex_unlock(unlock);
-
        return ret;
 
 }
 
        struct drm_i915_private *i915 = vma->vm->i915;
        unsigned int supported = INTEL_INFO(i915)->page_sizes;
        struct drm_i915_gem_object *obj = vma->obj;
-       int err = 0;
+       int err;
+
+       /* We have to wait for the async bind to complete before our asserts */
+       err = i915_vma_sync(vma);
+       if (err)
+               return err;
 
        if (!HAS_PAGE_SIZES(i915, vma->page_sizes.sg)) {
                pr_err("unsupported page_sizes.sg=%u, supported=%u\n",
                        goto out_unpin;
                }
 
-               err = i915_vma_bind(vma, I915_CACHE_NONE, PIN_UPDATE);
+               err = i915_vma_bind(vma, I915_CACHE_NONE, PIN_UPDATE, NULL);
                if (err)
                        goto out_unpin;
 
 
        if (err)
                goto skip_request;
 
-       i915_vma_unpin(batch);
-       i915_vma_close(batch);
-       i915_vma_put(batch);
-
+       i915_vma_unpin_and_release(&batch, 0);
        i915_vma_unpin(vma);
 
        *rq_out = i915_request_get(rq);
 err_request:
        i915_request_add(rq);
 err_batch:
-       i915_vma_unpin(batch);
-       i915_vma_put(batch);
+       i915_vma_unpin_and_release(&batch, 0);
 err_vma:
        i915_vma_unpin(vma);
 
        if (err)
                goto skip_request;
 
-       i915_vma_unpin(vma);
-       i915_vma_close(vma);
-       i915_vma_put(vma);
+       i915_vma_unpin_and_release(&vma, 0);
 
        i915_request_add(rq);
 
 
                goto out;
        }
 
-       mutex_lock(&i915->drm.struct_mutex);
        wakeref = intel_runtime_pm_get(&i915->runtime_pm);
 
        if (1) {
 
 out_unlock:
        intel_runtime_pm_put(&i915->runtime_pm, wakeref);
-       mutex_unlock(&i915->drm.struct_mutex);
        i915_gem_object_unpin_pages(obj);
 out:
        i915_gem_object_put(obj);
                goto out;
        }
 
-       mutex_lock(&i915->drm.struct_mutex);
        wakeref = intel_runtime_pm_get(&i915->runtime_pm);
 
        count = 0;
        pr_info("%s: Completed %lu trials\n", __func__, count);
 
        intel_runtime_pm_put(&i915->runtime_pm, wakeref);
-       mutex_unlock(&i915->drm.struct_mutex);
        i915_gem_object_unpin_pages(obj);
 out:
        i915_gem_object_put(obj);
 
 
        i915_request_add(rq);
 
-       i915_vma_unpin(batch);
-       i915_vma_close(batch);
-       i915_vma_put(batch);
+       i915_vma_unpin_and_release(&batch, 0);
 
        return 0;
 
 err_request:
        i915_request_add(rq);
 err_batch:
-       i915_vma_unpin(batch);
-       i915_vma_put(batch);
+       i915_vma_unpin_and_release(&batch, 0);
        return err;
 }
 
 
        with_intel_runtime_pm(&i915->runtime_pm, wakeref) {
                struct intel_uncore *uncore = gt->uncore;
+               unsigned long flags;
 
-               spin_lock_irq(&uncore->lock);
+               spin_lock_irqsave(&uncore->lock, flags);
                intel_uncore_posting_read_fw(uncore,
                                             RING_HEAD(RENDER_RING_BASE));
-               spin_unlock_irq(&uncore->lock);
+               spin_unlock_irqrestore(&uncore->lock, flags);
        }
 }
 
 
 {
        struct intel_ring *ring = container_of(ref, typeof(*ring), ref);
 
-       i915_vma_close(ring->vma);
        i915_vma_put(ring->vma);
-
        kfree(ring);
 }
 
 static void __ring_context_fini(struct intel_context *ce)
 {
-       i915_gem_object_put(ce->state->obj);
+       i915_vma_put(ce->state);
 }
 
 static void ring_context_destroy(struct kref *ref)
 
 {
        struct evict_vma *arg = data;
        struct i915_address_space *vm = arg->vma->vm;
-       struct drm_i915_private *i915 = vm->i915;
        struct drm_mm_node evict = arg->vma->node;
        int err;
 
        complete(&arg->completion);
 
-       mutex_lock(&i915->drm.struct_mutex);
+       mutex_lock(&vm->mutex);
        err = i915_gem_evict_for_node(vm, &evict, 0);
-       mutex_unlock(&i915->drm.struct_mutex);
+       mutex_unlock(&vm->mutex);
 
        return err;
 }
 static int evict_fence(void *data)
 {
        struct evict_vma *arg = data;
-       struct drm_i915_private *i915 = arg->vma->vm->i915;
        int err;
 
        complete(&arg->completion);
 
-       mutex_lock(&i915->drm.struct_mutex);
-
        /* Mark the fence register as dirty to force the mmio update. */
        err = i915_gem_object_set_tiling(arg->vma->obj, I915_TILING_Y, 512);
        if (err) {
                pr_err("Invalid Y-tiling settings; err:%d\n", err);
-               goto out_unlock;
+               return err;
        }
 
        err = i915_vma_pin(arg->vma, 0, 0, PIN_GLOBAL | PIN_MAPPABLE);
        if (err) {
                pr_err("Unable to pin vma for Y-tiled fence; err:%d\n", err);
-               goto out_unlock;
+               return err;
        }
 
        err = i915_vma_pin_fence(arg->vma);
        i915_vma_unpin(arg->vma);
        if (err) {
                pr_err("Unable to pin Y-tiled fence; err:%d\n", err);
-               goto out_unlock;
+               return err;
        }
 
        i915_vma_unpin_fence(arg->vma);
 
-out_unlock:
-       mutex_unlock(&i915->drm.struct_mutex);
-
-       return err;
+       return 0;
 }
 
 static int __igt_reset_evict_vma(struct intel_gt *gt,
 
                flags = PIN_MAPPABLE;
        }
 
-       mutex_lock(&dev_priv->drm.struct_mutex);
+       mutex_lock(&dev_priv->ggtt.vm.mutex);
        mmio_hw_access_pre(dev_priv);
        ret = i915_gem_gtt_insert(&dev_priv->ggtt.vm, node,
                                  size, I915_GTT_PAGE_SIZE,
                                  I915_COLOR_UNEVICTABLE,
                                  start, end, flags);
        mmio_hw_access_post(dev_priv);
-       mutex_unlock(&dev_priv->drm.struct_mutex);
+       mutex_unlock(&dev_priv->ggtt.vm.mutex);
        if (ret)
                gvt_err("fail to alloc %s gm space from host\n",
                        high_gm ? "high" : "low");
 
        return 0;
 out_free_aperture:
-       mutex_lock(&dev_priv->drm.struct_mutex);
+       mutex_lock(&dev_priv->ggtt.vm.mutex);
        drm_mm_remove_node(&vgpu->gm.low_gm_node);
-       mutex_unlock(&dev_priv->drm.struct_mutex);
+       mutex_unlock(&dev_priv->ggtt.vm.mutex);
        return ret;
 }
 
 {
        struct drm_i915_private *dev_priv = vgpu->gvt->dev_priv;
 
-       mutex_lock(&dev_priv->drm.struct_mutex);
+       mutex_lock(&dev_priv->ggtt.vm.mutex);
        drm_mm_remove_node(&vgpu->gm.low_gm_node);
        drm_mm_remove_node(&vgpu->gm.high_gm_node);
-       mutex_unlock(&dev_priv->drm.struct_mutex);
+       mutex_unlock(&dev_priv->ggtt.vm.mutex);
 }
 
 /**
 
        if (!retire)
                return;
 
+       GEM_BUG_ON(rcu_access_pointer(ref->excl));
        rbtree_postorder_for_each_entry_safe(it, n, &root, node) {
                GEM_BUG_ON(i915_active_request_isset(&it->base));
                kmem_cache_free(global.slab_cache, it);
        ref->flags = 0;
        ref->active = active;
        ref->retire = retire;
+
+       ref->excl = NULL;
        ref->tree = RB_ROOT;
        ref->cache = NULL;
        init_llist_head(&ref->preallocated_barriers);
        return err;
 }
 
+static void excl_cb(struct dma_fence *f, struct dma_fence_cb *cb)
+{
+       struct i915_active *ref = container_of(cb, typeof(*ref), excl_cb);
+
+       RCU_INIT_POINTER(ref->excl, NULL);
+       dma_fence_put(f);
+
+       active_retire(ref);
+}
+
+void i915_active_set_exclusive(struct i915_active *ref, struct dma_fence *f)
+{
+       /* We expect the caller to manage the exclusive timeline ordering */
+       GEM_BUG_ON(i915_active_is_idle(ref));
+
+       dma_fence_get(f);
+
+       rcu_read_lock();
+       if (rcu_access_pointer(ref->excl)) {
+               struct dma_fence *old;
+
+               old = dma_fence_get_rcu_safe(&ref->excl);
+               if (old) {
+                       if (dma_fence_remove_callback(old, &ref->excl_cb))
+                               atomic_dec(&ref->count);
+                       dma_fence_put(old);
+               }
+       }
+       rcu_read_unlock();
+
+       atomic_inc(&ref->count);
+       rcu_assign_pointer(ref->excl, f);
+
+       if (dma_fence_add_callback(f, &ref->excl_cb, excl_cb)) {
+               RCU_INIT_POINTER(ref->excl, NULL);
+               atomic_dec(&ref->count);
+               dma_fence_put(f);
+       }
+}
+
 int i915_active_acquire(struct i915_active *ref)
 {
        int err;
        __active_ungrab(ref);
 }
 
+static int excl_wait(struct i915_active *ref)
+{
+       struct dma_fence *old;
+       int err = 0;
+
+       if (!rcu_access_pointer(ref->excl))
+               return 0;
+
+       rcu_read_lock();
+       old = dma_fence_get_rcu_safe(&ref->excl);
+       rcu_read_unlock();
+       if (old) {
+               err = dma_fence_wait(old, true);
+               dma_fence_put(old);
+       }
+
+       return err;
+}
+
 int i915_active_wait(struct i915_active *ref)
 {
        struct active_node *it, *n;
                return 0;
        }
 
+       err = excl_wait(ref);
+       if (err)
+               goto out;
+
        rbtree_postorder_for_each_entry_safe(it, n, &ref->tree, node) {
                if (is_barrier(&it->base)) { /* unconnected idle-barrier */
                        err = -EBUSY;
                        break;
        }
 
+out:
        __active_retire(ref);
        if (err)
                return err;
 
 int i915_request_await_active(struct i915_request *rq, struct i915_active *ref)
 {
-       struct active_node *it, *n;
-       int err;
-
-       if (RB_EMPTY_ROOT(&ref->tree))
-               return 0;
+       int err = 0;
 
-       /* await allocates and so we need to avoid hitting the shrinker */
-       err = i915_active_acquire(ref);
-       if (err)
-               return err;
+       if (rcu_access_pointer(ref->excl)) {
+               struct dma_fence *fence;
 
-       mutex_lock(&ref->mutex);
-       rbtree_postorder_for_each_entry_safe(it, n, &ref->tree, node) {
-               err = i915_request_await_active_request(rq, &it->base);
-               if (err)
-                       break;
+               rcu_read_lock();
+               fence = dma_fence_get_rcu_safe(&ref->excl);
+               rcu_read_unlock();
+               if (fence) {
+                       err = i915_request_await_dma_fence(rq, fence);
+                       dma_fence_put(fence);
+               }
        }
-       mutex_unlock(&ref->mutex);
 
-       i915_active_release(ref);
+       /* In the future we may choose to await on all fences */
+
        return err;
 }
 
 
        return i915_active_ref(ref, i915_request_timeline(rq), rq);
 }
 
+void i915_active_set_exclusive(struct i915_active *ref, struct dma_fence *f);
+
+static inline bool i915_active_has_exclusive(struct i915_active *ref)
+{
+       return rcu_access_pointer(ref->excl);
+}
+
 int i915_active_wait(struct i915_active *ref);
 
 int i915_request_await_active(struct i915_request *rq,
 
 #define _I915_ACTIVE_TYPES_H_
 
 #include <linux/atomic.h>
+#include <linux/dma-fence.h>
 #include <linux/llist.h>
 #include <linux/mutex.h>
 #include <linux/rbtree.h>
        struct mutex mutex;
        atomic_t count;
 
+       /* Preallocated "exclusive" node */
+       struct dma_fence __rcu *excl;
+       struct dma_fence_cb excl_cb;
+
        unsigned long flags;
 #define I915_ACTIVE_GRAB_BIT 0
 
 
        if (ret)
                DRM_ERROR("failed to re-enable GGTT\n");
 
-       mutex_lock(&dev_priv->drm.struct_mutex);
        i915_gem_restore_gtt_mappings(dev_priv);
        i915_gem_restore_fences(dev_priv);
-       mutex_unlock(&dev_priv->drm.struct_mutex);
 
        intel_csr_ucode_resume(dev_priv);
 
 
 #include "intel_pm.h"
 
 static int
-insert_mappable_node(struct i915_ggtt *ggtt,
-                     struct drm_mm_node *node, u32 size)
+insert_mappable_node(struct i915_ggtt *ggtt, struct drm_mm_node *node, u32 size)
 {
+       int err;
+
+       err = mutex_lock_interruptible(&ggtt->vm.mutex);
+       if (err)
+               return err;
+
        memset(node, 0, sizeof(*node));
-       return drm_mm_insert_node_in_range(&ggtt->vm.mm, node,
-                                          size, 0, I915_COLOR_UNEVICTABLE,
-                                          0, ggtt->mappable_end,
-                                          DRM_MM_INSERT_LOW);
+       err = drm_mm_insert_node_in_range(&ggtt->vm.mm, node,
+                                         size, 0, I915_COLOR_UNEVICTABLE,
+                                         0, ggtt->mappable_end,
+                                         DRM_MM_INSERT_LOW);
+
+       mutex_unlock(&ggtt->vm.mutex);
+
+       return err;
 }
 
 static void
-remove_mappable_node(struct drm_mm_node *node)
+remove_mappable_node(struct i915_ggtt *ggtt, struct drm_mm_node *node)
 {
+       mutex_lock(&ggtt->vm.mutex);
        drm_mm_remove_node(node);
+       mutex_unlock(&ggtt->vm.mutex);
 }
 
 int
        struct i915_vma *vma;
        u64 pinned;
 
-       mutex_lock(&ggtt->vm.mutex);
+       if (mutex_lock_interruptible(&ggtt->vm.mutex))
+               return -EINTR;
 
        pinned = ggtt->vm.reserved;
        list_for_each_entry(vma, &ggtt->vm.bound_list, vm_link)
        LIST_HEAD(still_in_list);
        int ret = 0;
 
-       lockdep_assert_held(&obj->base.dev->struct_mutex);
-
        spin_lock(&obj->vma.lock);
        while (!ret && (vma = list_first_entry_or_null(&obj->vma.list,
                                                       struct i915_vma,
                                                       obj_link))) {
+               struct i915_address_space *vm = vma->vm;
+
+               ret = -EBUSY;
+               if (!i915_vm_tryopen(vm))
+                       break;
+
                list_move_tail(&vma->obj_link, &still_in_list);
                spin_unlock(&obj->vma.lock);
 
-               ret = -EBUSY;
                if (flags & I915_GEM_OBJECT_UNBIND_ACTIVE ||
                    !i915_vma_is_active(vma))
                        ret = i915_vma_unbind(vma);
 
+               i915_vm_close(vm);
                spin_lock(&obj->vma.lock);
        }
        list_splice(&still_in_list, &obj->vma.list);
        u64 remain, offset;
        int ret;
 
-       ret = mutex_lock_interruptible(&i915->drm.struct_mutex);
-       if (ret)
-               return ret;
-
        wakeref = intel_runtime_pm_get(&i915->runtime_pm);
        vma = ERR_PTR(-ENODEV);
        if (!i915_gem_object_is_tiled(obj))
        } else {
                ret = insert_mappable_node(ggtt, &node, PAGE_SIZE);
                if (ret)
-                       goto out_unlock;
+                       goto out_rpm;
                GEM_BUG_ON(!drm_mm_node_allocated(&node));
        }
 
-       mutex_unlock(&i915->drm.struct_mutex);
-
        ret = i915_gem_object_lock_interruptible(obj);
        if (ret)
                goto out_unpin;
 
        i915_gem_object_unlock_fence(obj, fence);
 out_unpin:
-       mutex_lock(&i915->drm.struct_mutex);
        if (drm_mm_node_allocated(&node)) {
                ggtt->vm.clear_range(&ggtt->vm, node.start, node.size);
-               remove_mappable_node(&node);
+               remove_mappable_node(ggtt, &node);
        } else {
                i915_vma_unpin(vma);
        }
-out_unlock:
+out_rpm:
        intel_runtime_pm_put(&i915->runtime_pm, wakeref);
-       mutex_unlock(&i915->drm.struct_mutex);
-
        return ret;
 }
 
        void __user *user_data;
        int ret;
 
-       ret = mutex_lock_interruptible(&i915->drm.struct_mutex);
-       if (ret)
-               return ret;
-
        if (i915_gem_object_has_struct_page(obj)) {
                /*
                 * Avoid waking the device up if we can fallback, as
                 * using the cache bypass of indirect GGTT access.
                 */
                wakeref = intel_runtime_pm_get_if_in_use(rpm);
-               if (!wakeref) {
-                       ret = -EFAULT;
-                       goto out_unlock;
-               }
+               if (!wakeref)
+                       return -EFAULT;
        } else {
                /* No backing pages, no fallback, we must force GGTT access */
                wakeref = intel_runtime_pm_get(rpm);
                GEM_BUG_ON(!drm_mm_node_allocated(&node));
        }
 
-       mutex_unlock(&i915->drm.struct_mutex);
-
        ret = i915_gem_object_lock_interruptible(obj);
        if (ret)
                goto out_unpin;
 
        i915_gem_object_unlock_fence(obj, fence);
 out_unpin:
-       mutex_lock(&i915->drm.struct_mutex);
        intel_gt_flush_ggtt_writes(ggtt->vm.gt);
        if (drm_mm_node_allocated(&node)) {
                ggtt->vm.clear_range(&ggtt->vm, node.start, node.size);
-               remove_mappable_node(&node);
+               remove_mappable_node(ggtt, &node);
        } else {
                i915_vma_unpin(vma);
        }
 out_rpm:
        intel_runtime_pm_put(rpm, wakeref);
-out_unlock:
-       mutex_unlock(&i915->drm.struct_mutex);
        return ret;
 }
 
        struct i915_vma *vma;
        int ret;
 
-       lockdep_assert_held(&obj->base.dev->struct_mutex);
-
        if (i915_gem_object_never_bind_ggtt(obj))
                return ERR_PTR(-ENODEV);
 
                                return ERR_PTR(-ENOSPC);
                }
 
-               WARN(i915_vma_is_pinned(vma),
-                    "bo is already pinned in ggtt with incorrect alignment:"
-                    " offset=%08x, req.alignment=%llx,"
-                    " req.map_and_fenceable=%d, vma->map_and_fenceable=%d\n",
-                    i915_ggtt_offset(vma), alignment,
-                    !!(flags & PIN_MAPPABLE),
-                    i915_vma_is_map_and_fenceable(vma));
                ret = i915_vma_unbind(vma);
                if (ret)
                        return ERR_PTR(ret);
        }
 
        if (ret == -EIO) {
-               mutex_lock(&dev_priv->drm.struct_mutex);
-
                /*
                 * Allow engines or uC initialisation to fail by marking the GPU
                 * as wedged. But we only want to do this when the GPU is angry,
                i915_gem_restore_gtt_mappings(dev_priv);
                i915_gem_restore_fences(dev_priv);
                intel_init_clock_gating(dev_priv);
-
-               mutex_unlock(&dev_priv->drm.struct_mutex);
        }
 
        i915_gem_drain_freed_objects(dev_priv);
 
         * bound by their active reference.
         */
        return i915_gem_wait_for_idle(i915,
-                                     I915_WAIT_INTERRUPTIBLE |
-                                     I915_WAIT_LOCKED,
+                                     I915_WAIT_INTERRUPTIBLE,
                                      MAX_SCHEDULE_TIMEOUT);
 }
 
        struct i915_vma *active;
        int ret;
 
-       lockdep_assert_held(&vm->i915->drm.struct_mutex);
+       lockdep_assert_held(&vm->mutex);
        trace_i915_gem_evict(vm, min_size, alignment, flags);
 
        /*
                                    min_size, alignment, color,
                                    start, end, mode);
 
-       /*
-        * Retire before we search the active list. Although we have
-        * reasonable accuracy in our retirement lists, we may have
-        * a stray pin (preventing eviction) that can only be resolved by
-        * retiring.
-        */
-       if (!(flags & PIN_NONBLOCK))
-               i915_retire_requests(dev_priv);
-
 search_again:
        active = NULL;
        INIT_LIST_HEAD(&eviction_list);
        list_for_each_entry_safe(vma, next, &eviction_list, evict_link) {
                __i915_vma_unpin(vma);
                if (ret == 0)
-                       ret = i915_vma_unbind(vma);
+                       ret = __i915_vma_unbind(vma);
        }
 
        while (ret == 0 && (node = drm_mm_scan_color_evict(&scan))) {
                vma = container_of(node, struct i915_vma, node);
-               ret = i915_vma_unbind(vma);
+               ret = __i915_vma_unbind(vma);
        }
 
        return ret;
        struct i915_vma *vma, *next;
        int ret = 0;
 
-       lockdep_assert_held(&vm->i915->drm.struct_mutex);
+       lockdep_assert_held(&vm->mutex);
        GEM_BUG_ON(!IS_ALIGNED(start, I915_GTT_PAGE_SIZE));
        GEM_BUG_ON(!IS_ALIGNED(end, I915_GTT_PAGE_SIZE));
 
        list_for_each_entry_safe(vma, next, &eviction_list, evict_link) {
                __i915_vma_unpin(vma);
                if (ret == 0)
-                       ret = i915_vma_unbind(vma);
+                       ret = __i915_vma_unbind(vma);
        }
 
        return ret;
        struct i915_vma *vma, *next;
        int ret;
 
-       lockdep_assert_held(&vm->i915->drm.struct_mutex);
+       lockdep_assert_held(&vm->mutex);
        trace_i915_gem_evict_vm(vm);
 
        /* Switch back to the default context in order to unpin
        }
 
        INIT_LIST_HEAD(&eviction_list);
-       mutex_lock(&vm->mutex);
        list_for_each_entry(vma, &vm->bound_list, vm_link) {
                if (i915_vma_is_pinned(vma))
                        continue;
                __i915_vma_pin(vma);
                list_add(&vma->evict_link, &eviction_list);
        }
-       mutex_unlock(&vm->mutex);
 
        ret = 0;
        list_for_each_entry_safe(vma, next, &eviction_list, evict_link) {
                __i915_vma_unpin(vma);
                if (ret == 0)
-                       ret = i915_vma_unbind(vma);
+                       ret = __i915_vma_unbind(vma);
        }
        return ret;
 }
 
                         i915_gem_object_get_tiling(vma->obj)))
                        return -EINVAL;
 
-               ret = i915_active_wait(&vma->active);
+               ret = i915_vma_sync(vma);
                if (ret)
                        return ret;
        }
 
        old = xchg(&fence->vma, NULL);
        if (old) {
-               ret = i915_active_wait(&old->active);
+               /* XXX Ideally we would move the waiting to outside the mutex */
+               ret = i915_vma_sync(old);
                if (ret) {
                        fence->vma = old;
                        return ret;
        return ERR_PTR(-EDEADLK);
 }
 
-static int __i915_vma_pin_fence(struct i915_vma *vma)
+int __i915_vma_pin_fence(struct i915_vma *vma)
 {
        struct i915_ggtt *ggtt = i915_vm_to_ggtt(vma->vm);
        struct i915_fence_reg *fence;
        struct i915_vma *set = i915_gem_object_is_tiled(vma->obj) ? vma : NULL;
        int err;
 
+       lockdep_assert_held(&vma->vm->mutex);
+
        /* Just update our place in the LRU if our fence is getting reused. */
        if (vma->fence) {
                fence = vma->fence;
 
 
 static int ppgtt_bind_vma(struct i915_vma *vma,
                          enum i915_cache_level cache_level,
-                         u32 unused)
+                         u32 flags)
 {
        u32 pte_flags;
        int err;
 
-       if (!i915_vma_is_bound(vma, I915_VMA_LOCAL_BIND)) {
+       if (flags & I915_VMA_ALLOC) {
                err = vma->vm->allocate_va_range(vma->vm,
                                                 vma->node.start, vma->size);
                if (err)
                        return err;
+
+               set_bit(I915_VMA_ALLOC_BIT, __i915_vma_flags(vma));
        }
 
        /* Applicable to VLV, and gen8+ */
        if (i915_gem_object_is_readonly(vma->obj))
                pte_flags |= PTE_READ_ONLY;
 
+       GEM_BUG_ON(!test_bit(I915_VMA_ALLOC_BIT, __i915_vma_flags(vma)));
        vma->vm->insert_entries(vma->vm, vma, cache_level, pte_flags);
        wmb();
 
 
 static void ppgtt_unbind_vma(struct i915_vma *vma)
 {
-       vma->vm->clear_range(vma->vm, vma->node.start, vma->size);
+       if (test_and_clear_bit(I915_VMA_ALLOC_BIT, __i915_vma_flags(vma)))
+               vma->vm->clear_range(vma->vm, vma->node.start, vma->size);
 }
 
 static int ppgtt_set_pages(struct i915_vma *vma)
        mutex_destroy(&vm->mutex);
 }
 
-static void ppgtt_destroy_vma(struct i915_address_space *vm)
+void __i915_vm_close(struct i915_address_space *vm)
 {
        struct i915_vma *vma, *vn;
 
-       mutex_lock(&vm->i915->drm.struct_mutex);
-       list_for_each_entry_safe(vma, vn, &vm->bound_list, vm_link)
+       mutex_lock(&vm->mutex);
+       list_for_each_entry_safe(vma, vn, &vm->bound_list, vm_link) {
+               struct drm_i915_gem_object *obj = vma->obj;
+
+               /* Keep the obj (and hence the vma) alive as _we_ destroy it */
+               if (!kref_get_unless_zero(&obj->base.refcount))
+                       continue;
+
+               atomic_and(~I915_VMA_PIN_MASK, &vma->flags);
+               WARN_ON(__i915_vma_unbind(vma));
                i915_vma_destroy(vma);
+
+               i915_gem_object_put(obj);
+       }
        GEM_BUG_ON(!list_empty(&vm->bound_list));
-       mutex_unlock(&vm->i915->drm.struct_mutex);
+       mutex_unlock(&vm->mutex);
 }
 
 static void __i915_vm_release(struct work_struct *work)
        struct i915_address_space *vm =
                container_of(work, struct i915_address_space, rcu.work);
 
-       ppgtt_destroy_vma(vm);
-
        vm->cleanup(vm);
        i915_address_space_fini(vm);
 
        GEM_BUG_ON(i915_is_ggtt(vm));
        trace_i915_ppgtt_release(vm);
 
-       vm->closed = true;
        queue_rcu_work(vm->i915->wq, &vm->rcu);
 }
 
 {
        kref_init(&vm->ref);
        INIT_RCU_WORK(&vm->rcu, __i915_vm_release);
+       atomic_set(&vm->open, 1);
 
        /*
         * The vm->mutex must be reclaim safe (for use in the shrinker).
 static void gen6_ppgtt_cleanup(struct i915_address_space *vm)
 {
        struct gen6_ppgtt *ppgtt = to_gen6_ppgtt(i915_vm_to_ppgtt(vm));
-       struct drm_i915_private *i915 = vm->i915;
 
-       /* FIXME remove the struct_mutex to bring the locking under control */
-       mutex_lock(&i915->drm.struct_mutex);
        i915_vma_destroy(ppgtt->vma);
-       mutex_unlock(&i915->drm.struct_mutex);
 
        gen6_ppgtt_free_pd(ppgtt);
        free_scratch(vm);
 
        i915_active_init(i915, &vma->active, NULL, NULL);
 
-       vma->vm = &ggtt->vm;
+       mutex_init(&vma->pages_mutex);
+       vma->vm = i915_vm_get(&ggtt->vm);
        vma->ops = &pd_vma_ops;
        vma->private = ppgtt;
 
        struct gen6_ppgtt *ppgtt = to_gen6_ppgtt(base);
        int err = 0;
 
-       GEM_BUG_ON(ppgtt->base.vm.closed);
+       GEM_BUG_ON(!atomic_read(&ppgtt->base.vm.open));
 
        /*
         * Workaround the limited maximum vma->pin_count and the aliasing_ppgtt
        if (flags & I915_VMA_LOCAL_BIND) {
                struct i915_ppgtt *alias = i915_vm_to_ggtt(vma->vm)->alias;
 
-               if (!i915_vma_is_bound(vma, I915_VMA_LOCAL_BIND)) {
+               if (flags & I915_VMA_ALLOC) {
                        ret = alias->vm.allocate_va_range(&alias->vm,
                                                          vma->node.start,
                                                          vma->size);
                        if (ret)
                                return ret;
+
+                       set_bit(I915_VMA_ALLOC_BIT, __i915_vma_flags(vma));
                }
 
+               GEM_BUG_ON(!test_bit(I915_VMA_ALLOC_BIT,
+                                    __i915_vma_flags(vma)));
                alias->vm.insert_entries(&alias->vm, vma,
                                         cache_level, pte_flags);
        }
                        vm->clear_range(vm, vma->node.start, vma->size);
        }
 
-       if (i915_vma_is_bound(vma, I915_VMA_LOCAL_BIND)) {
+       if (test_and_clear_bit(I915_VMA_ALLOC_BIT, __i915_vma_flags(vma))) {
                struct i915_address_space *vm =
                        &i915_vm_to_ggtt(vma->vm)->alias->vm;
 
 
 static void fini_aliasing_ppgtt(struct i915_ggtt *ggtt)
 {
-       struct drm_i915_private *i915 = ggtt->vm.i915;
        struct i915_ppgtt *ppgtt;
 
-       mutex_lock(&i915->drm.struct_mutex);
-
        ppgtt = fetch_and_zero(&ggtt->alias);
        if (!ppgtt)
-               goto out;
+               return;
 
        i915_vm_put(&ppgtt->vm);
 
        ggtt->vm.vma_ops.bind_vma   = ggtt_bind_vma;
        ggtt->vm.vma_ops.unbind_vma = ggtt_unbind_vma;
-
-out:
-       mutex_unlock(&i915->drm.struct_mutex);
 }
 
 static int ggtt_reserve_guc_top(struct i915_ggtt *ggtt)
 
 static void ggtt_cleanup_hw(struct i915_ggtt *ggtt)
 {
-       struct drm_i915_private *i915 = ggtt->vm.i915;
        struct i915_vma *vma, *vn;
 
-       ggtt->vm.closed = true;
+       atomic_set(&ggtt->vm.open, 0);
 
        rcu_barrier(); /* flush the RCU'ed__i915_vm_release */
-       flush_workqueue(i915->wq);
+       flush_workqueue(ggtt->vm.i915->wq);
 
-       mutex_lock(&i915->drm.struct_mutex);
+       mutex_lock(&ggtt->vm.mutex);
 
        list_for_each_entry_safe(vma, vn, &ggtt->vm.bound_list, vm_link)
-               WARN_ON(i915_vma_unbind(vma));
+               WARN_ON(__i915_vma_unbind(vma));
 
        if (drm_mm_node_allocated(&ggtt->error_capture))
                drm_mm_remove_node(&ggtt->error_capture);
 
        ggtt_release_guc_top(ggtt);
-
-       if (drm_mm_initialized(&ggtt->vm.mm)) {
-               intel_vgt_deballoon(ggtt);
-               i915_address_space_fini(&ggtt->vm);
-       }
+       intel_vgt_deballoon(ggtt);
 
        ggtt->vm.cleanup(&ggtt->vm);
 
-       mutex_unlock(&i915->drm.struct_mutex);
+       mutex_unlock(&ggtt->vm.mutex);
+       i915_address_space_fini(&ggtt->vm);
 
        arch_phys_wc_del(ggtt->mtrr);
        io_mapping_fini(&ggtt->iomap);
 static int ggtt_init_hw(struct i915_ggtt *ggtt)
 {
        struct drm_i915_private *i915 = ggtt->vm.i915;
-       int ret = 0;
-
-       mutex_lock(&i915->drm.struct_mutex);
 
        i915_address_space_init(&ggtt->vm, VM_CLASS_GGTT);
 
                                ggtt->gmadr.start,
                                ggtt->mappable_end)) {
                ggtt->vm.cleanup(&ggtt->vm);
-               ret = -EIO;
-               goto out;
+               return -EIO;
        }
 
        ggtt->mtrr = arch_phys_wc_add(ggtt->gmadr.start, ggtt->mappable_end);
 
        i915_ggtt_init_fences(ggtt);
 
-out:
-       mutex_unlock(&i915->drm.struct_mutex);
-
-       return ret;
+       return 0;
 }
 
 /**
 {
        struct i915_vma *vma, *vn;
        bool flush = false;
+       int open;
 
        intel_gt_check_and_clear_faults(ggtt->vm.gt);
 
 
        /* First fill our portion of the GTT with scratch pages */
        ggtt->vm.clear_range(&ggtt->vm, 0, ggtt->vm.total);
-       ggtt->vm.closed = true; /* skip rewriting PTE on VMA unbind */
+
+       /* Skip rewriting PTE on VMA unbind. */
+       open = atomic_xchg(&ggtt->vm.open, 0);
 
        /* clflush objects bound into the GGTT and rebind them. */
        list_for_each_entry_safe(vma, vn, &ggtt->vm.bound_list, vm_link) {
                if (!i915_vma_is_bound(vma, I915_VMA_GLOBAL_BIND))
                        continue;
 
-               mutex_unlock(&ggtt->vm.mutex);
-
-               if (!i915_vma_unbind(vma))
-                       goto lock;
+               if (!__i915_vma_unbind(vma))
+                       continue;
 
+               clear_bit(I915_VMA_GLOBAL_BIND_BIT, __i915_vma_flags(vma));
                WARN_ON(i915_vma_bind(vma,
                                      obj ? obj->cache_level : 0,
-                                     PIN_UPDATE));
+                                     PIN_GLOBAL, NULL));
                if (obj) { /* only used during resume => exclusive access */
                        flush |= fetch_and_zero(&obj->write_domain);
                        obj->read_domains |= I915_GEM_DOMAIN_GTT;
                }
-
-lock:
-               mutex_lock(&ggtt->vm.mutex);
        }
 
-       ggtt->vm.closed = false;
+       atomic_set(&ggtt->vm.open, open);
        ggtt->invalidate(ggtt);
 
        mutex_unlock(&ggtt->vm.mutex);
        u64 offset;
        int err;
 
-       lockdep_assert_held(&vm->i915->drm.struct_mutex);
+       lockdep_assert_held(&vm->mutex);
+
        GEM_BUG_ON(!size);
        GEM_BUG_ON(!IS_ALIGNED(size, I915_GTT_PAGE_SIZE));
        GEM_BUG_ON(alignment && !is_power_of_2(alignment));
 
 
        unsigned int bind_async_flags;
 
-       bool closed;
+       /*
+        * Each active user context has its own address space (in full-ppgtt).
+        * Since the vm may be shared between multiple contexts, we count how
+        * many contexts keep us "open". Once open hits zero, we are closed
+        * and do not allow any new attachments, and proceed to shutdown our
+        * vma and page directories.
+        */
+       atomic_t open;
 
        struct mutex mutex; /* protects vma and our lists */
 #define VM_CLASS_GGTT 0
        kref_put(&vm->ref, i915_vm_release);
 }
 
+static inline struct i915_address_space *
+i915_vm_open(struct i915_address_space *vm)
+{
+       GEM_BUG_ON(!atomic_read(&vm->open));
+       atomic_inc(&vm->open);
+       return i915_vm_get(vm);
+}
+
+static inline bool
+i915_vm_tryopen(struct i915_address_space *vm)
+{
+       if (atomic_add_unless(&vm->open, 1, 0))
+               return i915_vm_get(vm);
+
+       return false;
+}
+
+void __i915_vm_close(struct i915_address_space *vm);
+
+static inline void
+i915_vm_close(struct i915_address_space *vm)
+{
+       GEM_BUG_ON(!atomic_read(&vm->open));
+       if (atomic_dec_and_test(&vm->open))
+               __i915_vm_close(vm);
+
+       i915_vm_put(vm);
+}
+
 int gen6_ppgtt_pin(struct i915_ppgtt *base);
 void gen6_ppgtt_unpin(struct i915_ppgtt *base);
 void gen6_ppgtt_unpin_all(struct i915_ppgtt *base);
 #define PIN_OFFSET_BIAS                BIT_ULL(6)
 #define PIN_OFFSET_FIXED       BIT_ULL(7)
 
-#define PIN_MBZ                        BIT_ULL(8) /* I915_VMA_PIN_OVERFLOW */
-#define PIN_GLOBAL             BIT_ULL(9) /* I915_VMA_GLOBAL_BIND */
-#define PIN_USER               BIT_ULL(10) /* I915_VMA_LOCAL_BIND */
-#define PIN_UPDATE             BIT_ULL(11)
+#define PIN_UPDATE             BIT_ULL(9)
+#define PIN_GLOBAL             BIT_ULL(10) /* I915_VMA_GLOBAL_BIND */
+#define PIN_USER               BIT_ULL(11) /* I915_VMA_LOCAL_BIND */
 
 #define PIN_OFFSET_MASK                (-I915_GTT_PAGE_SIZE)
 
 
 static struct intel_context *oa_pin_context(struct i915_perf_stream *stream)
 {
        struct i915_gem_engines_iter it;
-       struct drm_i915_private *i915 = stream->dev_priv;
        struct i915_gem_context *ctx = stream->ctx;
        struct intel_context *ce;
        int err;
 
-       err = i915_mutex_lock_interruptible(&i915->drm);
-       if (err)
-               return ERR_PTR(err);
-
        for_each_gem_engine(ce, i915_gem_context_lock_engines(ctx), it) {
                if (ce->engine->class != RENDER_CLASS)
                        continue;
        }
        i915_gem_context_unlock_engines(ctx);
 
-       mutex_unlock(&i915->drm.struct_mutex);
-       if (err)
-               return ERR_PTR(err);
-
        return stream->pinned_ctx;
 }
 
  */
 static void oa_put_render_ctx_id(struct i915_perf_stream *stream)
 {
-       struct drm_i915_private *dev_priv = stream->dev_priv;
        struct intel_context *ce;
 
        stream->specific_ctx_id = INVALID_CTX_ID;
        stream->specific_ctx_id_mask = 0;
 
        ce = fetch_and_zero(&stream->pinned_ctx);
-       if (ce) {
-               mutex_lock(&dev_priv->drm.struct_mutex);
+       if (ce)
                intel_context_unpin(ce);
-               mutex_unlock(&dev_priv->drm.struct_mutex);
-       }
 }
 
 static void
 free_oa_buffer(struct i915_perf_stream *stream)
 {
-       struct drm_i915_private *i915 = stream->dev_priv;
-
-       mutex_lock(&i915->drm.struct_mutex);
-
        i915_vma_unpin_and_release(&stream->oa_buffer.vma,
                                   I915_VMA_RELEASE_MAP);
 
-       mutex_unlock(&i915->drm.struct_mutex);
-
        stream->oa_buffer.vaddr = NULL;
 }
 
        if (WARN_ON(stream->oa_buffer.vma))
                return -ENODEV;
 
-       ret = i915_mutex_lock_interruptible(&dev_priv->drm);
-       if (ret)
-               return ret;
-
        BUILD_BUG_ON_NOT_POWER_OF_2(OA_BUFFER_SIZE);
        BUILD_BUG_ON(OA_BUFFER_SIZE < SZ_128K || OA_BUFFER_SIZE > SZ_16M);
 
        bo = i915_gem_object_create_shmem(dev_priv, OA_BUFFER_SIZE);
        if (IS_ERR(bo)) {
                DRM_ERROR("Failed to allocate OA buffer\n");
-               ret = PTR_ERR(bo);
-               goto unlock;
+               return PTR_ERR(bo);
        }
 
        i915_gem_object_set_cache_coherency(bo, I915_CACHE_LLC);
                         i915_ggtt_offset(stream->oa_buffer.vma),
                         stream->oa_buffer.vaddr);
 
-       goto unlock;
+       return 0;
 
 err_unpin:
        __i915_vma_unpin(vma);
        stream->oa_buffer.vaddr = NULL;
        stream->oa_buffer.vma = NULL;
 
-unlock:
-       mutex_unlock(&dev_priv->drm.struct_mutex);
        return ret;
 }
 
 
 
 #include "i915_drv.h"
 #include "i915_globals.h"
+#include "i915_sw_fence_work.h"
 #include "i915_trace.h"
 #include "i915_vma.h"
 
        if (vma == NULL)
                return ERR_PTR(-ENOMEM);
 
-       vma->vm = vm;
+       mutex_init(&vma->pages_mutex);
+       vma->vm = i915_vm_get(vm);
        vma->ops = &vm->vma_ops;
        vma->obj = obj;
        vma->resv = obj->base.resv;
  * Once created, the VMA is kept until either the object is freed, or the
  * address space is closed.
  *
- * Must be called with struct_mutex held.
- *
  * Returns the vma, or an error pointer.
  */
 struct i915_vma *
        struct i915_vma *vma;
 
        GEM_BUG_ON(view && !i915_is_ggtt(vm));
-       GEM_BUG_ON(vm->closed);
+       GEM_BUG_ON(!atomic_read(&vm->open));
 
        spin_lock(&obj->vma.lock);
        vma = vma_lookup(obj, vm, view);
        return vma;
 }
 
+struct i915_vma_work {
+       struct dma_fence_work base;
+       struct i915_vma *vma;
+       enum i915_cache_level cache_level;
+       unsigned int flags;
+};
+
+static int __vma_bind(struct dma_fence_work *work)
+{
+       struct i915_vma_work *vw = container_of(work, typeof(*vw), base);
+       struct i915_vma *vma = vw->vma;
+       int err;
+
+       err = vma->ops->bind_vma(vma, vw->cache_level, vw->flags);
+       if (err)
+               atomic_or(I915_VMA_ERROR, &vma->flags);
+
+       if (vma->obj)
+               __i915_gem_object_unpin_pages(vma->obj);
+
+       return err;
+}
+
+static const struct dma_fence_work_ops bind_ops = {
+       .name = "bind",
+       .work = __vma_bind,
+};
+
+struct i915_vma_work *i915_vma_work(void)
+{
+       struct i915_vma_work *vw;
+
+       vw = kzalloc(sizeof(*vw), GFP_KERNEL);
+       if (!vw)
+               return NULL;
+
+       dma_fence_work_init(&vw->base, &bind_ops);
+       vw->base.dma.error = -EAGAIN; /* disable the worker by default */
+
+       return vw;
+}
+
 /**
  * i915_vma_bind - Sets up PTEs for an VMA in it's corresponding address space.
  * @vma: VMA to map
  * @cache_level: mapping cache level
  * @flags: flags like global or local mapping
+ * @work: preallocated worker for allocating and binding the PTE
  *
  * DMA addresses are taken from the scatter-gather table of this object (or of
  * this VMA in case of non-default GGTT views) and PTE entries set up.
  * Note that DMA addresses are also the only part of the SG table we care about.
  */
-int i915_vma_bind(struct i915_vma *vma, enum i915_cache_level cache_level,
-                 u32 flags)
+int i915_vma_bind(struct i915_vma *vma,
+                 enum i915_cache_level cache_level,
+                 u32 flags,
+                 struct i915_vma_work *work)
 {
        u32 bind_flags;
        u32 vma_flags;
        if (GEM_DEBUG_WARN_ON(!flags))
                return -EINVAL;
 
-       bind_flags = 0;
-       if (flags & PIN_GLOBAL)
-               bind_flags |= I915_VMA_GLOBAL_BIND;
-       if (flags & PIN_USER)
-               bind_flags |= I915_VMA_LOCAL_BIND;
+       bind_flags = flags;
+       bind_flags &= I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND;
 
        vma_flags = atomic_read(&vma->flags);
        vma_flags &= I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND;
        GEM_BUG_ON(!vma->pages);
 
        trace_i915_vma_bind(vma, bind_flags);
-       ret = vma->ops->bind_vma(vma, cache_level, bind_flags);
-       if (ret)
-               return ret;
+       if (work && (bind_flags & ~vma_flags) & vma->vm->bind_async_flags) {
+               work->vma = vma;
+               work->cache_level = cache_level;
+               work->flags = bind_flags | I915_VMA_ALLOC;
+
+               /*
+                * Note we only want to chain up to the migration fence on
+                * the pages (not the object itself). As we don't track that,
+                * yet, we have to use the exclusive fence instead.
+                *
+                * Also note that we do not want to track the async vma as
+                * part of the obj->resv->excl_fence as it only affects
+                * execution and not content or object's backing store lifetime.
+                */
+               GEM_BUG_ON(i915_active_has_exclusive(&vma->active));
+               i915_active_set_exclusive(&vma->active, &work->base.dma);
+               work->base.dma.error = 0; /* enable the queue_work() */
+
+               if (vma->obj)
+                       __i915_gem_object_pin_pages(vma->obj);
+       } else {
+               GEM_BUG_ON((bind_flags & ~vma_flags) & vma->vm->bind_async_flags);
+               ret = vma->ops->bind_vma(vma, cache_level, bind_flags);
+               if (ret)
+                       return ret;
+       }
 
        atomic_or(bind_flags, &vma->flags);
        return 0;
 
        /* Access through the GTT requires the device to be awake. */
        assert_rpm_wakelock_held(&vma->vm->i915->runtime_pm);
-
-       lockdep_assert_held(&vma->vm->i915->drm.struct_mutex);
-       if (WARN_ON(!i915_vma_is_map_and_fenceable(vma))) {
+       if (GEM_WARN_ON(!i915_vma_is_map_and_fenceable(vma))) {
                err = -ENODEV;
                goto err;
        }
        GEM_BUG_ON(!i915_vma_is_ggtt(vma));
        GEM_BUG_ON(!i915_vma_is_bound(vma, I915_VMA_GLOBAL_BIND));
 
-       ptr = vma->iomap;
+       ptr = READ_ONCE(vma->iomap);
        if (ptr == NULL) {
                ptr = io_mapping_map_wc(&i915_vm_to_ggtt(vma->vm)->iomap,
                                        vma->node.start,
                        goto err;
                }
 
-               vma->iomap = ptr;
+               if (unlikely(cmpxchg(&vma->iomap, NULL, ptr))) {
+                       io_mapping_unmap(ptr);
+                       ptr = vma->iomap;
+               }
        }
 
        __i915_vma_pin(vma);
 
 void i915_vma_flush_writes(struct i915_vma *vma)
 {
-       if (!i915_vma_has_ggtt_write(vma))
-               return;
-
-       intel_gt_flush_ggtt_writes(vma->vm->gt);
-
-       i915_vma_unset_ggtt_write(vma);
+       if (i915_vma_unset_ggtt_write(vma))
+               intel_gt_flush_ggtt_writes(vma->vm->gt);
 }
 
 void i915_vma_unpin_iomap(struct i915_vma *vma)
 {
-       lockdep_assert_held(&vma->vm->i915->drm.struct_mutex);
-
        GEM_BUG_ON(vma->iomap == NULL);
 
        i915_vma_flush_writes(vma);
        if (!drm_mm_node_allocated(&vma->node))
                return false;
 
+       if (test_bit(I915_VMA_ERROR_BIT, __i915_vma_flags(vma)))
+               return true;
+
        if (vma->node.size < size)
                return true;
 
 static int
 i915_vma_insert(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
 {
-       struct drm_i915_private *dev_priv = vma->vm->i915;
        unsigned long color;
        u64 start, end;
        int ret;
 
        end = vma->vm->total;
        if (flags & PIN_MAPPABLE)
-               end = min_t(u64, end, dev_priv->ggtt.mappable_end);
+               end = min_t(u64, end, i915_vm_to_ggtt(vma->vm)->mappable_end);
        if (flags & PIN_ZONE_4G)
                end = min_t(u64, end, (1ULL << 32) - I915_GTT_PAGE_SIZE);
        GEM_BUG_ON(!IS_ALIGNED(end, I915_GTT_PAGE_SIZE));
        }
 
        color = 0;
-       if (vma->obj) {
-               ret = i915_gem_object_pin_pages(vma->obj);
-               if (ret)
-                       return ret;
-
-               if (i915_vm_has_cache_coloring(vma->vm))
-                       color = vma->obj->cache_level;
-       }
-
-       GEM_BUG_ON(vma->pages);
-
-       ret = vma->ops->set_pages(vma);
-       if (ret)
-               goto err_unpin;
+       if (vma->obj && i915_vm_has_cache_coloring(vma->vm))
+               color = vma->obj->cache_level;
 
        if (flags & PIN_OFFSET_FIXED) {
                u64 offset = flags & PIN_OFFSET_MASK;
                if (!IS_ALIGNED(offset, alignment) ||
-                   range_overflows(offset, size, end)) {
-                       ret = -EINVAL;
-                       goto err_clear;
-               }
+                   range_overflows(offset, size, end))
+                       return -EINVAL;
 
                ret = i915_gem_gtt_reserve(vma->vm, &vma->node,
                                           size, offset, color,
                                           flags);
                if (ret)
-                       goto err_clear;
+                       return ret;
        } else {
                /*
                 * We only support huge gtt pages through the 48b PPGTT,
                                          size, alignment, color,
                                          start, end, flags);
                if (ret)
-                       goto err_clear;
+                       return ret;
 
                GEM_BUG_ON(vma->node.start < start);
                GEM_BUG_ON(vma->node.start + vma->node.size > end);
        GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
        GEM_BUG_ON(!i915_gem_valid_gtt_space(vma, color));
 
-       mutex_lock(&vma->vm->mutex);
        list_add_tail(&vma->vm_link, &vma->vm->bound_list);
-       mutex_unlock(&vma->vm->mutex);
 
        if (vma->obj) {
+               atomic_inc(&vma->obj->mm.pages_pin_count);
                atomic_inc(&vma->obj->bind_count);
                assert_bind_count(vma->obj);
        }
 
        return 0;
-
-err_clear:
-       vma->ops->clear_pages(vma);
-err_unpin:
-       if (vma->obj)
-               i915_gem_object_unpin_pages(vma->obj);
-       return ret;
 }
 
 static void
        GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
        GEM_BUG_ON(i915_vma_is_bound(vma, I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND));
 
-       vma->ops->clear_pages(vma);
-
-       mutex_lock(&vma->vm->mutex);
-       drm_mm_remove_node(&vma->node);
        list_del(&vma->vm_link);
-       mutex_unlock(&vma->vm->mutex);
 
        /*
         * Since the unbound list is global, only move to that list if
                i915_gem_object_unpin_pages(obj);
                assert_bind_count(obj);
        }
+
+       drm_mm_remove_node(&vma->node);
 }
 
-int __i915_vma_do_pin(struct i915_vma *vma,
-                     u64 size, u64 alignment, u64 flags)
+static bool try_qad_pin(struct i915_vma *vma, unsigned int flags)
 {
-       const unsigned int bound = atomic_read(&vma->flags);
-       int ret;
+       unsigned int bound;
+       bool pinned = true;
 
-       lockdep_assert_held(&vma->vm->i915->drm.struct_mutex);
-       GEM_BUG_ON((flags & (PIN_GLOBAL | PIN_USER)) == 0);
-       GEM_BUG_ON((flags & PIN_GLOBAL) && !i915_vma_is_ggtt(vma));
+       bound = atomic_read(&vma->flags);
+       do {
+               if (unlikely(flags & ~bound))
+                       return false;
 
-       if (WARN_ON(bound & I915_VMA_PIN_OVERFLOW)) {
-               ret = -EBUSY;
-               goto err_unpin;
+               if (unlikely(bound & (I915_VMA_OVERFLOW | I915_VMA_ERROR)))
+                       return false;
+
+               if (!(bound & I915_VMA_PIN_MASK))
+                       goto unpinned;
+
+               GEM_BUG_ON(((bound + 1) & I915_VMA_PIN_MASK) == 0);
+       } while (!atomic_try_cmpxchg(&vma->flags, &bound, bound + 1));
+
+       return true;
+
+unpinned:
+       /*
+        * If pin_count==0, but we are bound, check under the lock to avoid
+        * racing with a concurrent i915_vma_unbind().
+        */
+       mutex_lock(&vma->vm->mutex);
+       do {
+               if (unlikely(bound & (I915_VMA_OVERFLOW | I915_VMA_ERROR))) {
+                       pinned = false;
+                       break;
+               }
+
+               if (unlikely(flags & ~bound)) {
+                       pinned = false;
+                       break;
+               }
+       } while (!atomic_try_cmpxchg(&vma->flags, &bound, bound + 1));
+       mutex_unlock(&vma->vm->mutex);
+
+       return pinned;
+}
+
+static int vma_get_pages(struct i915_vma *vma)
+{
+       int err = 0;
+
+       if (atomic_add_unless(&vma->pages_count, 1, 0))
+               return 0;
+
+       /* Allocations ahoy! */
+       if (mutex_lock_interruptible(&vma->pages_mutex))
+               return -EINTR;
+
+       if (!atomic_read(&vma->pages_count)) {
+               if (vma->obj) {
+                       err = i915_gem_object_pin_pages(vma->obj);
+                       if (err)
+                               goto unlock;
+               }
+
+               err = vma->ops->set_pages(vma);
+               if (err)
+                       goto unlock;
        }
+       atomic_inc(&vma->pages_count);
 
-       if ((bound & I915_VMA_BIND_MASK) == 0) {
-               ret = i915_vma_insert(vma, size, alignment, flags);
-               if (ret)
-                       goto err_unpin;
+unlock:
+       mutex_unlock(&vma->pages_mutex);
+
+       return err;
+}
+
+static void __vma_put_pages(struct i915_vma *vma, unsigned int count)
+{
+       /* We allocate under vma_get_pages, so beware the shrinker */
+       mutex_lock_nested(&vma->pages_mutex, SINGLE_DEPTH_NESTING);
+       GEM_BUG_ON(atomic_read(&vma->pages_count) < count);
+       if (atomic_sub_return(count, &vma->pages_count) == 0) {
+               vma->ops->clear_pages(vma);
+               GEM_BUG_ON(vma->pages);
+               if (vma->obj)
+                       i915_gem_object_unpin_pages(vma->obj);
        }
-       GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
+       mutex_unlock(&vma->pages_mutex);
+}
 
-       ret = i915_vma_bind(vma, vma->obj ? vma->obj->cache_level : 0, flags);
-       if (ret)
-               goto err_remove;
+static void vma_put_pages(struct i915_vma *vma)
+{
+       if (atomic_add_unless(&vma->pages_count, -1, 1))
+               return;
+
+       __vma_put_pages(vma, 1);
+}
+
+static void vma_unbind_pages(struct i915_vma *vma)
+{
+       unsigned int count;
+
+       lockdep_assert_held(&vma->vm->mutex);
+
+       /* The upper portion of pages_count is the number of bindings */
+       count = atomic_read(&vma->pages_count);
+       count >>= I915_VMA_PAGES_BIAS;
+       GEM_BUG_ON(!count);
+
+       __vma_put_pages(vma, count | count << I915_VMA_PAGES_BIAS);
+}
+
+int i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
+{
+       struct i915_vma_work *work = NULL;
+       unsigned int bound;
+       int err;
+
+       BUILD_BUG_ON(PIN_GLOBAL != I915_VMA_GLOBAL_BIND);
+       BUILD_BUG_ON(PIN_USER != I915_VMA_LOCAL_BIND);
+
+       GEM_BUG_ON(flags & PIN_UPDATE);
+       GEM_BUG_ON(!(flags & (PIN_USER | PIN_GLOBAL)));
+
+       /* First try and grab the pin without rebinding the vma */
+       if (try_qad_pin(vma, flags & I915_VMA_BIND_MASK))
+               return 0;
+
+       err = vma_get_pages(vma);
+       if (err)
+               return err;
+
+       if (flags & vma->vm->bind_async_flags) {
+               work = i915_vma_work();
+               if (!work) {
+                       err = -ENOMEM;
+                       goto err_pages;
+               }
+       }
+
+       /* No more allocations allowed once we hold vm->mutex */
+       err = mutex_lock_interruptible(&vma->vm->mutex);
+       if (err)
+               goto err_fence;
+
+       bound = atomic_read(&vma->flags);
+       if (unlikely(bound & I915_VMA_ERROR)) {
+               err = -ENOMEM;
+               goto err_unlock;
+       }
+
+       if (unlikely(!((bound + 1) & I915_VMA_PIN_MASK))) {
+               err = -EAGAIN; /* pins are meant to be fairly temporary */
+               goto err_unlock;
+       }
+
+       if (unlikely(!(flags & ~bound & I915_VMA_BIND_MASK))) {
+               __i915_vma_pin(vma);
+               goto err_unlock;
+       }
+
+       err = i915_active_acquire(&vma->active);
+       if (err)
+               goto err_unlock;
+
+       if (!(bound & I915_VMA_BIND_MASK)) {
+               err = i915_vma_insert(vma, size, alignment, flags);
+               if (err)
+                       goto err_active;
+
+               if (i915_is_ggtt(vma->vm))
+                       __i915_vma_set_map_and_fenceable(vma);
+       }
 
-       GEM_BUG_ON(!i915_vma_is_bound(vma, I915_VMA_BIND_MASK));
+       GEM_BUG_ON(!vma->pages);
+       err = i915_vma_bind(vma,
+                           vma->obj ? vma->obj->cache_level : 0,
+                           flags, work);
+       if (err)
+               goto err_remove;
 
-       if ((bound ^ atomic_read(&vma->flags)) & I915_VMA_GLOBAL_BIND)
-               __i915_vma_set_map_and_fenceable(vma);
+       /* There should only be at most 2 active bindings (user, global) */
+       GEM_BUG_ON(bound + I915_VMA_PAGES_ACTIVE < bound);
+       atomic_add(I915_VMA_PAGES_ACTIVE, &vma->pages_count);
+       list_move_tail(&vma->vm_link, &vma->vm->bound_list);
 
+       __i915_vma_pin(vma);
+       GEM_BUG_ON(!i915_vma_is_pinned(vma));
+       GEM_BUG_ON(!i915_vma_is_bound(vma, flags));
        GEM_BUG_ON(i915_vma_misplaced(vma, size, alignment, flags));
-       return 0;
 
 err_remove:
-       if ((bound & I915_VMA_BIND_MASK) == 0) {
+       if (!i915_vma_is_bound(vma, I915_VMA_BIND_MASK))
                i915_vma_remove(vma);
-               GEM_BUG_ON(vma->pages);
-               GEM_BUG_ON(atomic_read(&vma->flags) & I915_VMA_BIND_MASK);
-       }
-err_unpin:
-       __i915_vma_unpin(vma);
-       return ret;
+err_active:
+       i915_active_release(&vma->active);
+err_unlock:
+       mutex_unlock(&vma->vm->mutex);
+err_fence:
+       if (work)
+               dma_fence_work_commit(&work->base);
+err_pages:
+       vma_put_pages(vma);
+       return err;
 }
 
 void i915_vma_close(struct i915_vma *vma)
 {
        struct drm_i915_private *i915 = vma->vm->i915;
 
-       if (!i915_vma_is_closed(vma))
-               return;
-
        spin_lock_irq(&i915->gt.closed_lock);
        list_del_init(&vma->closed_link);
        spin_unlock_irq(&i915->gt.closed_lock);
 
 void i915_vma_reopen(struct i915_vma *vma)
 {
-       __i915_vma_remove_closed(vma);
+       if (i915_vma_is_closed(vma))
+               __i915_vma_remove_closed(vma);
 }
 
-static void __i915_vma_destroy(struct i915_vma *vma)
+void i915_vma_destroy(struct i915_vma *vma)
 {
-       GEM_BUG_ON(drm_mm_node_allocated(&vma->node));
-       GEM_BUG_ON(vma->fence);
+       if (drm_mm_node_allocated(&vma->node)) {
+               mutex_lock(&vma->vm->mutex);
+               atomic_and(~I915_VMA_PIN_MASK, &vma->flags);
+               WARN_ON(__i915_vma_unbind(vma));
+               mutex_unlock(&vma->vm->mutex);
+               GEM_BUG_ON(drm_mm_node_allocated(&vma->node));
+       }
+       GEM_BUG_ON(i915_vma_is_active(vma));
 
        if (vma->obj) {
                struct drm_i915_gem_object *obj = vma->obj;
 
                spin_lock(&obj->vma.lock);
                list_del(&vma->obj_link);
-               rb_erase(&vma->obj_node, &vma->obj->vma.tree);
+               rb_erase(&vma->obj_node, &obj->vma.tree);
                spin_unlock(&obj->vma.lock);
        }
 
-       i915_active_fini(&vma->active);
-
-       i915_vma_free(vma);
-}
-
-void i915_vma_destroy(struct i915_vma *vma)
-{
-       lockdep_assert_held(&vma->vm->i915->drm.struct_mutex);
-
-       GEM_BUG_ON(i915_vma_is_pinned(vma));
-
        __i915_vma_remove_closed(vma);
+       i915_vm_put(vma->vm);
 
-       WARN_ON(i915_vma_unbind(vma));
-       GEM_BUG_ON(i915_vma_is_active(vma));
-
-       __i915_vma_destroy(vma);
+       i915_active_fini(&vma->active);
+       i915_vma_free(vma);
 }
 
 void i915_vma_parked(struct drm_i915_private *i915)
 
        spin_lock_irq(&i915->gt.closed_lock);
        list_for_each_entry_safe(vma, next, &i915->gt.closed_vma, closed_link) {
-               list_del_init(&vma->closed_link);
+               struct drm_i915_gem_object *obj = vma->obj;
+               struct i915_address_space *vm = vma->vm;
+
+               /* XXX All to avoid keeping a reference on i915_vma itself */
+
+               if (!kref_get_unless_zero(&obj->base.refcount))
+                       continue;
+
+               if (!i915_vm_tryopen(vm)) {
+                       i915_gem_object_put(obj);
+                       obj = NULL;
+               }
+
                spin_unlock_irq(&i915->gt.closed_lock);
 
-               i915_vma_destroy(vma);
+               if (obj) {
+                       i915_vma_destroy(vma);
+                       i915_gem_object_put(obj);
+               }
 
+               i915_vm_close(vm);
+
+               /* Restart after dropping lock */
                spin_lock_irq(&i915->gt.closed_lock);
+               next = list_first_entry(&i915->gt.closed_vma,
+                                       typeof(*next), closed_link);
        }
        spin_unlock_irq(&i915->gt.closed_lock);
 }
                list_del(&vma->obj->userfault_link);
 }
 
+int __i915_vma_move_to_active(struct i915_vma *vma, struct i915_request *rq)
+{
+       int err;
+
+       GEM_BUG_ON(!i915_vma_is_pinned(vma));
+
+       /* Wait for the vma to be bound before we start! */
+       err = i915_request_await_active(rq, &vma->active);
+       if (err)
+               return err;
+
+       return i915_active_add_request(&vma->active, rq);
+}
+
 int i915_vma_move_to_active(struct i915_vma *vma,
                            struct i915_request *rq,
                            unsigned int flags)
        struct drm_i915_gem_object *obj = vma->obj;
        int err;
 
-       assert_vma_held(vma);
        assert_object_held(obj);
-       GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
 
-       /*
-        * Add a reference if we're newly entering the active list.
-        * The order in which we add operations to the retirement queue is
-        * vital here: mark_active adds to the start of the callback list,
-        * such that subsequent callbacks are called first. Therefore we
-        * add the active reference first and queue for it to be dropped
-        * *last*.
-        */
-       err = i915_active_add_request(&vma->active, rq);
+       err = __i915_vma_move_to_active(vma, rq);
        if (unlikely(err))
                return err;
 
        return 0;
 }
 
-int i915_vma_unbind(struct i915_vma *vma)
+int __i915_vma_unbind(struct i915_vma *vma)
 {
        int ret;
 
-       lockdep_assert_held(&vma->vm->i915->drm.struct_mutex);
+       lockdep_assert_held(&vma->vm->mutex);
 
        /*
         * First wait upon any activity as retiring the request may
         * have side-effects such as unpinning or even unbinding this vma.
+        *
+        * XXX Actually waiting under the vm->mutex is a hinderance and
+        * should be pipelined wherever possible. In cases where that is
+        * unavoidable, we should lift the wait to before the mutex.
         */
-       might_sleep();
-       if (i915_vma_is_active(vma)) {
-               /*
-                * When a closed VMA is retired, it is unbound - eek.
-                * In order to prevent it from being recursively closed,
-                * take a pin on the vma so that the second unbind is
-                * aborted.
-                *
-                * Even more scary is that the retire callback may free
-                * the object (last active vma). To prevent the explosion
-                * we defer the actual object free to a worker that can
-                * only proceed once it acquires the struct_mutex (which
-                * we currently hold, therefore it cannot free this object
-                * before we are finished).
-                */
-               __i915_vma_pin(vma);
-               ret = i915_active_wait(&vma->active);
-               __i915_vma_unpin(vma);
-               if (ret)
-                       return ret;
-       }
-       GEM_BUG_ON(i915_vma_is_active(vma));
+       ret = i915_vma_sync(vma);
+       if (ret)
+               return ret;
 
        if (i915_vma_is_pinned(vma)) {
                vma_print_allocator(vma, "is pinned");
                GEM_BUG_ON(i915_vma_has_ggtt_write(vma));
 
                /* release the fence reg _after_ flushing */
-               mutex_lock(&vma->vm->mutex);
                ret = i915_vma_revoke_fence(vma);
-               mutex_unlock(&vma->vm->mutex);
                if (ret)
                        return ret;
 
                /* Force a pagefault for domain tracking on next user access */
-               mutex_lock(&vma->vm->mutex);
                i915_vma_revoke_mmap(vma);
-               mutex_unlock(&vma->vm->mutex);
 
                __i915_vma_iounmap(vma);
                clear_bit(I915_VMA_CAN_FENCE_BIT, __i915_vma_flags(vma));
        GEM_BUG_ON(vma->fence);
        GEM_BUG_ON(i915_vma_has_userfault(vma));
 
-       if (likely(!vma->vm->closed)) {
+       if (likely(atomic_read(&vma->vm->open))) {
                trace_i915_vma_unbind(vma);
                vma->ops->unbind_vma(vma);
        }
-       atomic_and(~I915_VMA_BIND_MASK, &vma->flags);
+       atomic_and(~(I915_VMA_BIND_MASK | I915_VMA_ERROR), &vma->flags);
 
+       vma_unbind_pages(vma);
        i915_vma_remove(vma);
 
        return 0;
 }
 
+int i915_vma_unbind(struct i915_vma *vma)
+{
+       struct i915_address_space *vm = vma->vm;
+       int err;
+
+       err = mutex_lock_interruptible(&vm->mutex);
+       if (err)
+               return err;
+
+       err = __i915_vma_unbind(vma);
+       mutex_unlock(&vm->mutex);
+
+       return err;
+}
+
 struct i915_vma *i915_vma_make_unshrinkable(struct i915_vma *vma)
 {
        i915_gem_object_make_unshrinkable(vma->obj);
 
         * exclusive cachelines of a single page, so a maximum of 64 possible
         * users.
         */
-#define I915_VMA_PIN_MASK 0xff
-#define I915_VMA_PIN_OVERFLOW_BIT 8
-#define I915_VMA_PIN_OVERFLOW  ((int)BIT(I915_VMA_PIN_OVERFLOW_BIT))
+#define I915_VMA_PIN_MASK 0x3ff
+#define I915_VMA_OVERFLOW 0x200
 
        /** Flags and address space this VMA is bound to */
-#define I915_VMA_GLOBAL_BIND_BIT 9
-#define I915_VMA_LOCAL_BIND_BIT 10
+#define I915_VMA_GLOBAL_BIND_BIT 10
+#define I915_VMA_LOCAL_BIND_BIT  11
 
 #define I915_VMA_GLOBAL_BIND   ((int)BIT(I915_VMA_GLOBAL_BIND_BIT))
 #define I915_VMA_LOCAL_BIND    ((int)BIT(I915_VMA_LOCAL_BIND_BIT))
 
-#define I915_VMA_BIND_MASK (I915_VMA_GLOBAL_BIND | \
-                           I915_VMA_LOCAL_BIND | \
-                           I915_VMA_PIN_OVERFLOW)
+#define I915_VMA_BIND_MASK (I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND)
 
-#define I915_VMA_GGTT_BIT      11
-#define I915_VMA_CAN_FENCE_BIT 12
-#define I915_VMA_USERFAULT_BIT 13
-#define I915_VMA_GGTT_WRITE_BIT        14
+#define I915_VMA_ALLOC_BIT     12
+#define I915_VMA_ALLOC         ((int)BIT(I915_VMA_ALLOC_BIT))
+
+#define I915_VMA_ERROR_BIT     13
+#define I915_VMA_ERROR         ((int)BIT(I915_VMA_ERROR_BIT))
+
+#define I915_VMA_GGTT_BIT      14
+#define I915_VMA_CAN_FENCE_BIT 15
+#define I915_VMA_USERFAULT_BIT 16
+#define I915_VMA_GGTT_WRITE_BIT        17
 
 #define I915_VMA_GGTT          ((int)BIT(I915_VMA_GGTT_BIT))
 #define I915_VMA_CAN_FENCE     ((int)BIT(I915_VMA_CAN_FENCE_BIT))
 
        struct i915_active active;
 
+#define I915_VMA_PAGES_BIAS 24
+#define I915_VMA_PAGES_ACTIVE (BIT(24) | 1)
+       atomic_t pages_count; /* number of active binds to the pages */
+       struct mutex pages_mutex; /* protect acquire/release of backing pages */
+
        /**
         * Support different GGTT views into the same object.
         * This means there can be multiple VMA mappings per object and per VM.
        return !i915_active_is_idle(&vma->active);
 }
 
+int __must_check __i915_vma_move_to_active(struct i915_vma *vma,
+                                          struct i915_request *rq);
 int __must_check i915_vma_move_to_active(struct i915_vma *vma,
                                         struct i915_request *rq,
                                         unsigned int flags);
        return memcmp(&vma->ggtt_view.partial, &view->partial, view->type);
 }
 
-int i915_vma_bind(struct i915_vma *vma, enum i915_cache_level cache_level,
-                 u32 flags);
+struct i915_vma_work *i915_vma_work(void);
+int i915_vma_bind(struct i915_vma *vma,
+                 enum i915_cache_level cache_level,
+                 u32 flags,
+                 struct i915_vma_work *work);
+
 bool i915_gem_valid_gtt_space(struct i915_vma *vma, unsigned long color);
 bool i915_vma_misplaced(const struct i915_vma *vma,
                        u64 size, u64 alignment, u64 flags);
 void __i915_vma_set_map_and_fenceable(struct i915_vma *vma);
 void i915_vma_revoke_mmap(struct i915_vma *vma);
+int __i915_vma_unbind(struct i915_vma *vma);
 int __must_check i915_vma_unbind(struct i915_vma *vma);
 void i915_vma_unlink_ctx(struct i915_vma *vma);
 void i915_vma_close(struct i915_vma *vma);
        dma_resv_unlock(vma->resv);
 }
 
-int __i915_vma_do_pin(struct i915_vma *vma,
-                     u64 size, u64 alignment, u64 flags);
-static inline int __must_check
-i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
-{
-       BUILD_BUG_ON(PIN_MBZ != I915_VMA_PIN_OVERFLOW);
-       BUILD_BUG_ON(PIN_GLOBAL != I915_VMA_GLOBAL_BIND);
-       BUILD_BUG_ON(PIN_USER != I915_VMA_LOCAL_BIND);
-
-       /* Pin early to prevent the shrinker/eviction logic from destroying
-        * our vma as we insert and bind.
-        */
-       if (likely(((atomic_inc_return(&vma->flags) ^ flags) & I915_VMA_BIND_MASK) == 0)) {
-               GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
-               GEM_BUG_ON(i915_vma_misplaced(vma, size, alignment, flags));
-               return 0;
-       }
-
-       return __i915_vma_do_pin(vma, size, alignment, flags);
-}
+int __must_check
+i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags);
 
 static inline int i915_vma_pin_count(const struct i915_vma *vma)
 {
 static inline void __i915_vma_pin(struct i915_vma *vma)
 {
        atomic_inc(&vma->flags);
-       GEM_BUG_ON(atomic_read(&vma->flags) & I915_VMA_PIN_OVERFLOW);
+       GEM_BUG_ON(!i915_vma_is_pinned(vma));
 }
 
 static inline void __i915_vma_unpin(struct i915_vma *vma)
 {
+       GEM_BUG_ON(!i915_vma_is_pinned(vma));
        atomic_dec(&vma->flags);
 }
 
 static inline void i915_vma_unpin(struct i915_vma *vma)
 {
-       GEM_BUG_ON(!i915_vma_is_pinned(vma));
        GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
        __i915_vma_unpin(vma);
 }
  * the caller must call i915_vma_unpin_iomap to relinquish the pinning
  * after the iomapping is no longer required.
  *
- * Callers must hold the struct_mutex.
- *
  * Returns a valid iomapped pointer or ERR_PTR.
  */
 void __iomem *i915_vma_pin_iomap(struct i915_vma *vma);
  *
  * Unpins the previously iomapped VMA from i915_vma_pin_iomap().
  *
- * Callers must hold the struct_mutex. This function is only valid to be
- * called on a VMA previously iomapped by the caller with i915_vma_pin_iomap().
+ * This function is only valid to be called on a VMA previously
+ * iomapped by the caller with i915_vma_pin_iomap().
  */
 void i915_vma_unpin_iomap(struct i915_vma *vma);
 
 int __must_check i915_vma_pin_fence(struct i915_vma *vma);
 int __must_check i915_vma_revoke_fence(struct i915_vma *vma);
 
+int __i915_vma_pin_fence(struct i915_vma *vma);
+
 static inline void __i915_vma_unpin_fence(struct i915_vma *vma)
 {
        GEM_BUG_ON(atomic_read(&vma->fence->pin_count) <= 0);
 static inline void
 i915_vma_unpin_fence(struct i915_vma *vma)
 {
-       /* lockdep_assert_held(&vma->vm->i915->drm.struct_mutex); */
        if (vma->fence)
                __i915_vma_unpin_fence(vma);
 }
 void i915_vma_make_shrinkable(struct i915_vma *vma);
 void i915_vma_make_purgeable(struct i915_vma *vma);
 
+static inline int i915_vma_sync(struct i915_vma *vma)
+{
+       /* Wait for the asynchronous bindings and pending GPU reads */
+       return i915_active_wait(&vma->active);
+}
+
 #endif
 
                intel_gt_sanitize(&i915->gt, false);
                i915_gem_sanitize(i915);
 
-               mutex_lock(&i915->drm.struct_mutex);
                i915_gem_restore_gtt_mappings(i915);
                i915_gem_restore_fences(i915);
-               mutex_unlock(&i915->drm.struct_mutex);
 
                i915_gem_resume(i915);
        }
 
 
 static void unpin_ggtt(struct drm_i915_private *i915)
 {
-       struct i915_ggtt *ggtt = &i915->ggtt;
        struct i915_vma *vma;
 
-       mutex_lock(&ggtt->vm.mutex);
        list_for_each_entry(vma, &i915->ggtt.vm.bound_list, vm_link)
                if (vma->obj->mm.quirked)
                        i915_vma_unpin(vma);
-       mutex_unlock(&ggtt->vm.mutex);
 }
 
 static void cleanup_objects(struct drm_i915_private *i915,
                i915_gem_object_put(obj);
        }
 
-       mutex_unlock(&i915->drm.struct_mutex);
-
        i915_gem_drain_freed_objects(i915);
-
-       mutex_lock(&i915->drm.struct_mutex);
 }
 
 static int igt_evict_something(void *arg)
                goto cleanup;
 
        /* Everything is pinned, nothing should happen */
+       mutex_lock(&ggtt->vm.mutex);
        err = i915_gem_evict_something(&ggtt->vm,
                                       I915_GTT_PAGE_SIZE, 0, 0,
                                       0, U64_MAX,
                                       0);
+       mutex_unlock(&ggtt->vm.mutex);
        if (err != -ENOSPC) {
                pr_err("i915_gem_evict_something failed on a full GGTT with err=%d\n",
                       err);
        unpin_ggtt(i915);
 
        /* Everything is unpinned, we should be able to evict something */
+       mutex_lock(&ggtt->vm.mutex);
        err = i915_gem_evict_something(&ggtt->vm,
                                       I915_GTT_PAGE_SIZE, 0, 0,
                                       0, U64_MAX,
                                       0);
+       mutex_unlock(&ggtt->vm.mutex);
        if (err) {
                pr_err("i915_gem_evict_something failed on a full GGTT with err=%d\n",
                       err);
                goto cleanup;
 
        /* Everything is pinned, nothing should happen */
+       mutex_lock(&ggtt->vm.mutex);
        err = i915_gem_evict_for_node(&ggtt->vm, &target, 0);
+       mutex_unlock(&ggtt->vm.mutex);
        if (err != -ENOSPC) {
                pr_err("i915_gem_evict_for_node on a full GGTT returned err=%d\n",
                       err);
        unpin_ggtt(i915);
 
        /* Everything is unpinned, we should be able to evict the node */
+       mutex_lock(&ggtt->vm.mutex);
        err = i915_gem_evict_for_node(&ggtt->vm, &target, 0);
+       mutex_unlock(&ggtt->vm.mutex);
        if (err) {
                pr_err("i915_gem_evict_for_node returned err=%d\n",
                       err);
        i915_vma_unpin(vma);
 
        /* Remove just the second vma */
+       mutex_lock(&ggtt->vm.mutex);
        err = i915_gem_evict_for_node(&ggtt->vm, &target, 0);
+       mutex_unlock(&ggtt->vm.mutex);
        if (err) {
                pr_err("[0]i915_gem_evict_for_node returned err=%d\n", err);
                goto cleanup;
         */
        target.color = I915_CACHE_L3_LLC;
 
+       mutex_lock(&ggtt->vm.mutex);
        err = i915_gem_evict_for_node(&ggtt->vm, &target, 0);
+       mutex_unlock(&ggtt->vm.mutex);
        if (!err) {
                pr_err("[1]i915_gem_evict_for_node returned err=%d\n", err);
                err = -EINVAL;
                goto cleanup;
 
        /* Everything is pinned, nothing should happen */
+       mutex_lock(&ggtt->vm.mutex);
        err = i915_gem_evict_vm(&ggtt->vm);
+       mutex_unlock(&ggtt->vm.mutex);
        if (err) {
                pr_err("i915_gem_evict_vm on a full GGTT returned err=%d]\n",
                       err);
 
        unpin_ggtt(i915);
 
+       mutex_lock(&ggtt->vm.mutex);
        err = i915_gem_evict_vm(&ggtt->vm);
+       mutex_unlock(&ggtt->vm.mutex);
        if (err) {
                pr_err("i915_gem_evict_vm on a full GGTT returned err=%d]\n",
                       err);
        if (!HAS_FULL_PPGTT(i915))
                return 0;
 
-       mutex_lock(&i915->drm.struct_mutex);
        wakeref = intel_runtime_pm_get(&i915->runtime_pm);
 
        /* Reserve a block so that we know we have enough to fit a few rq */
        memset(&hole, 0, sizeof(hole));
+       mutex_lock(&i915->ggtt.vm.mutex);
        err = i915_gem_gtt_insert(&i915->ggtt.vm, &hole,
                                  PRETEND_GGTT_SIZE, 0, I915_COLOR_UNEVICTABLE,
                                  0, i915->ggtt.vm.total,
        do {
                struct reserved *r;
 
+               mutex_unlock(&i915->ggtt.vm.mutex);
                r = kcalloc(1, sizeof(*r), GFP_KERNEL);
+               mutex_lock(&i915->ggtt.vm.mutex);
                if (!r) {
                        err = -ENOMEM;
                        goto out_locked;
                count++;
        } while (1);
        drm_mm_remove_node(&hole);
-       mutex_unlock(&i915->drm.struct_mutex);
+       mutex_unlock(&i915->ggtt.vm.mutex);
        pr_info("Filled GGTT with %lu 1MiB nodes\n", count);
 
        /* Overfill the GGTT with context objects and so try to evict one. */
                        break;
        }
 
-       mutex_lock(&i915->drm.struct_mutex);
+       mutex_lock(&i915->ggtt.vm.mutex);
 out_locked:
        if (igt_flush_test(i915, I915_WAIT_LOCKED))
                err = -EIO;
        }
        if (drm_mm_node_allocated(&hole))
                drm_mm_remove_node(&hole);
+       mutex_unlock(&i915->ggtt.vm.mutex);
        intel_runtime_pm_put(&i915->runtime_pm, wakeref);
-       mutex_unlock(&i915->drm.struct_mutex);
 
        return err;
 }
        if (!i915)
                return -ENOMEM;
 
-       mutex_lock(&i915->drm.struct_mutex);
        with_intel_runtime_pm(&i915->runtime_pm, wakeref)
                err = i915_subtests(tests, i915);
 
-       mutex_unlock(&i915->drm.struct_mutex);
-
        drm_dev_put(&i915->drm);
        return err;
 }
 
 
 static void cleanup_freed_objects(struct drm_i915_private *i915)
 {
-       /*
-        * As we may hold onto the struct_mutex for inordinate lengths of
-        * time, the NMI khungtaskd detector may fire for the free objects
-        * worker.
-        */
-       mutex_unlock(&i915->drm.struct_mutex);
-
        i915_gem_drain_freed_objects(i915);
-
-       mutex_lock(&i915->drm.struct_mutex);
 }
 
 static void fake_free_pages(struct drm_i915_gem_object *obj,
                i915_vma_unpin(vma);
                addr += size;
 
+               /*
+                * Since we are injecting allocation faults at random intervals,
+                * wait for this allocation to complete before we change the
+                * faultinjection.
+                */
+               err = i915_vma_sync(vma);
+               if (err)
+                       break;
+
                if (igt_timeout(end_time,
                                "%s timed out at ofset %llx [%llx - %llx]\n",
                                __func__, addr, hole_start, hole_end)) {
        if (IS_ERR(file))
                return PTR_ERR(file);
 
-       mutex_lock(&dev_priv->drm.struct_mutex);
        ppgtt = i915_ppgtt_create(dev_priv);
        if (IS_ERR(ppgtt)) {
                err = PTR_ERR(ppgtt);
-               goto out_unlock;
+               goto out_free;
        }
        GEM_BUG_ON(offset_in_page(ppgtt->vm.total));
-       GEM_BUG_ON(ppgtt->vm.closed);
+       GEM_BUG_ON(!atomic_read(&ppgtt->vm.open));
 
        err = func(dev_priv, &ppgtt->vm, 0, ppgtt->vm.total, end_time);
 
        i915_vm_put(&ppgtt->vm);
-out_unlock:
-       mutex_unlock(&dev_priv->drm.struct_mutex);
 
+out_free:
        mock_file_free(dev_priv, file);
        return err;
 }
        IGT_TIMEOUT(end_time);
        int err = 0;
 
-       mutex_lock(&i915->drm.struct_mutex);
 restart:
        list_sort(NULL, &ggtt->vm.mm.hole_stack, sort_holes);
        drm_mm_for_each_hole(node, &ggtt->vm.mm, hole_start, hole_end) {
                last = hole_end;
                goto restart;
        }
-       mutex_unlock(&i915->drm.struct_mutex);
 
        return err;
 }
        unsigned int *order, n;
        int err;
 
-       mutex_lock(&i915->drm.struct_mutex);
-
        obj = i915_gem_object_create_internal(i915, PAGE_SIZE);
-       if (IS_ERR(obj)) {
-               err = PTR_ERR(obj);
-               goto out_unlock;
-       }
+       if (IS_ERR(obj))
+               return PTR_ERR(obj);
 
        err = i915_gem_object_pin_pages(obj);
        if (err)
        i915_gem_object_unpin_pages(obj);
 out_free:
        i915_gem_object_put(obj);
-out_unlock:
-       mutex_unlock(&i915->drm.struct_mutex);
        return err;
 }
 
        atomic_inc(&obj->bind_count); /* track for eviction later */
        __i915_gem_object_pin_pages(obj);
 
+       GEM_BUG_ON(vma->pages);
+       atomic_set(&vma->pages_count, I915_VMA_PAGES_ACTIVE);
+       __i915_gem_object_pin_pages(obj);
        vma->pages = obj->mm.pages;
 
        mutex_lock(&vma->vm->mutex);
                        goto out;
                }
 
+               mutex_lock(&ggtt->vm.mutex);
                err = i915_gem_gtt_reserve(&ggtt->vm, &vma->node,
                                           obj->base.size,
                                           total,
                                           obj->cache_level,
                                           0);
+               mutex_unlock(&ggtt->vm.mutex);
                if (err) {
                        pr_err("i915_gem_gtt_reserve (pass 1) failed at %llu/%llu with err=%d\n",
                               total, ggtt->vm.total, err);
                        goto out;
                }
 
+               mutex_lock(&ggtt->vm.mutex);
                err = i915_gem_gtt_reserve(&ggtt->vm, &vma->node,
                                           obj->base.size,
                                           total,
                                           obj->cache_level,
                                           0);
+               mutex_unlock(&ggtt->vm.mutex);
                if (err) {
                        pr_err("i915_gem_gtt_reserve (pass 2) failed at %llu/%llu with err=%d\n",
                               total, ggtt->vm.total, err);
                                           2 * I915_GTT_PAGE_SIZE,
                                           I915_GTT_MIN_ALIGNMENT);
 
+               mutex_lock(&ggtt->vm.mutex);
                err = i915_gem_gtt_reserve(&ggtt->vm, &vma->node,
                                           obj->base.size,
                                           offset,
                                           obj->cache_level,
                                           0);
+               mutex_unlock(&ggtt->vm.mutex);
                if (err) {
                        pr_err("i915_gem_gtt_reserve (pass 3) failed at %llu/%llu with err=%d\n",
                               total, ggtt->vm.total, err);
 
        /* Check a couple of obviously invalid requests */
        for (ii = invalid_insert; ii->size; ii++) {
+               mutex_lock(&ggtt->vm.mutex);
                err = i915_gem_gtt_insert(&ggtt->vm, &tmp,
                                          ii->size, ii->alignment,
                                          I915_COLOR_UNEVICTABLE,
                                          ii->start, ii->end,
                                          0);
+               mutex_unlock(&ggtt->vm.mutex);
                if (err != -ENOSPC) {
                        pr_err("Invalid i915_gem_gtt_insert(.size=%llx, .alignment=%llx, .start=%llx, .end=%llx) succeeded (err=%d)\n",
                               ii->size, ii->alignment, ii->start, ii->end,
                        goto out;
                }
 
+               mutex_lock(&ggtt->vm.mutex);
                err = i915_gem_gtt_insert(&ggtt->vm, &vma->node,
                                          obj->base.size, 0, obj->cache_level,
                                          0, ggtt->vm.total,
                                          0);
+               mutex_unlock(&ggtt->vm.mutex);
                if (err == -ENOSPC) {
                        /* maxed out the GGTT space */
                        i915_gem_object_put(obj);
                        goto out;
                }
 
+               mutex_lock(&ggtt->vm.mutex);
                err = i915_gem_gtt_insert(&ggtt->vm, &vma->node,
                                          obj->base.size, 0, obj->cache_level,
                                          0, ggtt->vm.total,
                                          0);
+               mutex_unlock(&ggtt->vm.mutex);
                if (err) {
                        pr_err("i915_gem_gtt_insert (pass 2) failed at %llu/%llu with err=%d\n",
                               total, ggtt->vm.total, err);
                        goto out;
                }
 
+               mutex_lock(&ggtt->vm.mutex);
                err = i915_gem_gtt_insert(&ggtt->vm, &vma->node,
                                          obj->base.size, 0, obj->cache_level,
                                          0, ggtt->vm.total,
                                          0);
+               mutex_unlock(&ggtt->vm.mutex);
                if (err) {
                        pr_err("i915_gem_gtt_insert (pass 3) failed at %llu/%llu with err=%d\n",
                               total, ggtt->vm.total, err);
        }
        mock_init_ggtt(i915, ggtt);
 
-       mutex_lock(&i915->drm.struct_mutex);
        err = i915_subtests(tests, ggtt);
+
+       mutex_lock(&i915->drm.struct_mutex);
        mock_device_flush(i915);
        mutex_unlock(&i915->drm.struct_mutex);
 
 
        if (err)
                goto err;
 
+       /* Force the wait wait now to avoid including it in the benchmark */
+       err = i915_vma_sync(vma);
+       if (err)
+               goto err_pin;
+
        return vma;
 
+err_pin:
+       i915_vma_unpin(vma);
 err:
        i915_gem_object_put(obj);
        return ERR_PTR(err);
 
        }
        mock_init_ggtt(i915, ggtt);
 
-       mutex_lock(&i915->drm.struct_mutex);
        err = i915_subtests(tests, ggtt);
+
+       mutex_lock(&i915->drm.struct_mutex);
        mock_device_flush(i915);
        mutex_unlock(&i915->drm.struct_mutex);
 
        if (IS_ERR(obj))
                return PTR_ERR(obj);
 
-       mutex_lock(&i915->drm.struct_mutex);
-
        wakeref = intel_runtime_pm_get(&i915->runtime_pm);
 
        for (t = types; *t; t++) {
 
 out:
        intel_runtime_pm_put(&i915->runtime_pm, wakeref);
-       mutex_unlock(&i915->drm.struct_mutex);
        i915_gem_object_put(obj);
 
        return err;