All of lore.kernel.org
 help / color / mirror / Atom feed
* [Intel-gfx] s/obj->mm.lock//
@ 2020-07-06  6:19 Chris Wilson
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 01/20] drm/i915: Preallocate stashes for vma page-directories Chris Wilson
                   ` (24 more replies)
  0 siblings, 25 replies; 65+ messages in thread
From: Chris Wilson @ 2020-07-06  6:19 UTC (permalink / raw)
  To: intel-gfx

This is the easy part; pulling reservation of multiple objects under an
ww acquire context. With one simple rule that eviction is handled by the
ww acquire context, we can carefully transition the driver over to using
eviction. Instead of feeding the acquire context everywhere, we make the
caller gather up all the objects they need to acquire into the context,
then acquire their backing store. The major boon here is that by
providing a clean set of objects (that we have not yet started to
acquire any auxiliary attachments for) to the acquire context, it can
handle all the EDEADLK processing for us [since it is a pure locking
operation and does not need to release attachments upon revoking the
locks].

As a sketch of what that would look like, to illustrate the major work
remaining:

static int evict(struct drm_i915_gem_object *obj, struct i915_acquire_ctx *ctx)
{
	struct intel_memory_region *mem = obj->mm->region;
	struct drm_i915_gem_object *swap; // struct i915_mm_bo *swap
	struct i915_request *rq;
	int err;

	/* swap = mem->create_eviction_target(obj); */
	swap = i915_gem_object_create_shmem(mem->i915, obj->base.size);
	if (IS_ERR(swap))
		return PTR_ERR(swap);

	err = dma_resv_lock_interruptible(swap, &ctx->ctx);
	GEM_BUG_ON(err == -EALREADY);
	if (err == -EDEADLK)
		goto out;

	/* Obviously swap has to be carefully chosen so that this may succeed */
	err = __i915_gem_object_get_pages_locked(swap);
	if (err)
		goto out_unlock;

	rq = pinned_evict_copy(ctx, obj, swap);
	if (IS_ERR(rq)) {
		err = PTR_ERR(rq);
		goto out_unlock;
	}

	err = i915_gem_revoke_mm(obj);
	if (err)
		goto out_request;

	/* Alternatively you could wait synchronously! */
	mem->release_blocks(&obj->mm->blocks, rq);
	i915_mm_bo_put(xchg(&obj->mm, i915_mm_bo_get(swap)));

	dma_resv_add_exclusive_fence(obj->base.resv, &rq->fence);
out_request:
	i915_request_put(rq);
out_unlock:
	dma_resv_unlock(swap);
out:
	i915_gem_object_put(swap);
	return err;
}

static int relock_all(struct i915_acquire_ctx *ctx)
{
	struct i915_acquire_link *lnk, *lock;
	int err;

	for (lnk = ctx->locked; lnk; lnk = lnk->next)
		dma_resv_unlock(lnk->obj->base.resv);

	lock = fetch_and_zero(&ctx->locked);
	while ((lnk = lock)) {
		struct drm_i915_gem_object *obj;

		obj = lnk->obj;
		lock = lnk->next;

		if (ctx->locked)
			err = dma_resv_lock_interruptible(obj->base.resv,
							  &ctx->ctx);
		else
			err = dma_resv_lock_slow_interruptible(obj->base.resv,
							       &ctx->ctx);
		GEM_BUG_ON(err == -EALREADY);
		if (err == -EDEADLK) {
			struct i915_acquire *old;

			while ((old = ctx->locked)) {
				dma_resv_unlock(old->obj->base.resv);
				ctx->locked = old->next;
				old->next = lock;
				lock = old;
			}

			lnk->next = lock;
			lock = lnk;
			continue;
		}
		if (err) {
			lock = lnk;
			break;
		}

		lnk->next = ctx->locked;
		ctx->locked = lnk;
	}

	while ((lnk = lock)) {
		lock = lnk->next;
		i915_gem_object_put(lnk->obj);
		i915_acquire_free(lnk);
	}

	return err;
}

int i915_acquire_mm(struct i915_acquire_ctx *ctx)
{
	struct i915_acquire_link *lnk;
	int n, err;

restart:
	for (lnk = ctx->locked; lnk; lnk = lnk->next) {
		for (n = 0;
		     !i915_gem_object_has_pages(lnk->obj);
		     n++) {
			struct drm_i915_gem_object *evictee = NULL;

			mem = get_preferred_memregion_for_object(lnk->obj, n);
			if (!mem)
				return -ENXIO;

			while (!i915_gem_object_get_pages(lnk->obj)) {
				struct i915_acquire_link *this;

				evictee = mem->get_eviction_candidate(mem,
								      evictee);
				if (!evictee)
					break;

				err = dma_resv_lock_interruptible(evictee,
								  &ctx->ctx);
				if (err == -EALREADY)
					continue; /* XXX fragmentation? */
				if (err == 0)
					err = evict(evictee);
				dma_resv_unlock(evictee);

				if (err == -EDEADLK) {
					err = relock_all(ctx);
					if (!err)
						goto restart;
				}
				if (err)
					return err;

				evictee = NULL;
			}
		}
	}

	return 0;
}

With the biggest task being seperating the GEM object from its backing
store, so that we can transparently exchange that backing store underneath
the user handles.

[There's also the challenge of *needing* to take ww_mutex underneath
userptr/mmu-notifier, but at the worst that's a bit of lockdep annotation
and a crafty if (page_maybe_dma_pinned)) in the LRU shrinker.]
-Chris


_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

* [Intel-gfx] [PATCH 01/20] drm/i915: Preallocate stashes for vma page-directories
  2020-07-06  6:19 [Intel-gfx] s/obj->mm.lock// Chris Wilson
@ 2020-07-06  6:19 ` Chris Wilson
  2020-07-06 18:15   ` Matthew Auld
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 02/20] drm/i915: Switch to object allocations for page directories Chris Wilson
                   ` (23 subsequent siblings)
  24 siblings, 1 reply; 65+ messages in thread
From: Chris Wilson @ 2020-07-06  6:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: Chris Wilson

We need to make the DMA allocations used for page directories to be
performed up front so that we can include those allocations in our
memory reservation pass. The downside is that we have to assume the
worst case, even before we know the final layout, and always allocate
enough page directories for this object, even when there will be overlap.

It should be noted that the lifetime for the page directories DMA is
more or less decoupled from individual fences as they will be shared
across objects across timelines.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 .../gpu/drm/i915/gem/i915_gem_client_blt.c    | 11 +--
 drivers/gpu/drm/i915/gt/gen6_ppgtt.c          | 38 +++------
 drivers/gpu/drm/i915/gt/gen8_ppgtt.c          | 77 +++++-------------
 drivers/gpu/drm/i915/gt/intel_ggtt.c          | 60 +++++++-------
 drivers/gpu/drm/i915/gt/intel_gtt.h           | 39 ++++++---
 drivers/gpu/drm/i915/gt/intel_ppgtt.c         | 80 ++++++++++++++++---
 drivers/gpu/drm/i915/i915_vma.c               | 29 ++++---
 drivers/gpu/drm/i915/selftests/i915_gem_gtt.c | 60 ++++++++------
 drivers/gpu/drm/i915/selftests/mock_gtt.c     | 22 ++---
 9 files changed, 230 insertions(+), 186 deletions(-)

diff --git a/drivers/gpu/drm/i915/gem/i915_gem_client_blt.c b/drivers/gpu/drm/i915/gem/i915_gem_client_blt.c
index 278664f831e7..947c8aa8e13e 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_client_blt.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_client_blt.c
@@ -32,12 +32,13 @@ static void vma_clear_pages(struct i915_vma *vma)
 	vma->pages = NULL;
 }
 
-static int vma_bind(struct i915_address_space *vm,
-		    struct i915_vma *vma,
-		    enum i915_cache_level cache_level,
-		    u32 flags)
+static void vma_bind(struct i915_address_space *vm,
+		     struct i915_vm_pt_stash *stash,
+		     struct i915_vma *vma,
+		     enum i915_cache_level cache_level,
+		     u32 flags)
 {
-	return vm->vma_ops.bind_vma(vm, vma, cache_level, flags);
+	vm->vma_ops.bind_vma(vm, stash, vma, cache_level, flags);
 }
 
 static void vma_unbind(struct i915_address_space *vm, struct i915_vma *vma)
diff --git a/drivers/gpu/drm/i915/gt/gen6_ppgtt.c b/drivers/gpu/drm/i915/gt/gen6_ppgtt.c
index 05497b50103f..35e2b698f9ed 100644
--- a/drivers/gpu/drm/i915/gt/gen6_ppgtt.c
+++ b/drivers/gpu/drm/i915/gt/gen6_ppgtt.c
@@ -177,16 +177,16 @@ static void gen6_flush_pd(struct gen6_ppgtt *ppgtt, u64 start, u64 end)
 	mutex_unlock(&ppgtt->flush);
 }
 
-static int gen6_alloc_va_range(struct i915_address_space *vm,
-			       u64 start, u64 length)
+static void gen6_alloc_va_range(struct i915_address_space *vm,
+				struct i915_vm_pt_stash *stash,
+				u64 start, u64 length)
 {
 	struct gen6_ppgtt *ppgtt = to_gen6_ppgtt(i915_vm_to_ppgtt(vm));
 	struct i915_page_directory * const pd = ppgtt->base.pd;
-	struct i915_page_table *pt, *alloc = NULL;
+	struct i915_page_table *pt;
 	intel_wakeref_t wakeref;
 	u64 from = start;
 	unsigned int pde;
-	int ret = 0;
 
 	wakeref = intel_runtime_pm_get(&vm->i915->runtime_pm);
 
@@ -197,21 +197,17 @@ static int gen6_alloc_va_range(struct i915_address_space *vm,
 		if (px_base(pt) == px_base(&vm->scratch[1])) {
 			spin_unlock(&pd->lock);
 
-			pt = fetch_and_zero(&alloc);
-			if (!pt)
-				pt = alloc_pt(vm);
-			if (IS_ERR(pt)) {
-				ret = PTR_ERR(pt);
-				goto unwind_out;
-			}
+			pt = stash->pt[0];
+			GEM_BUG_ON(!pt);
 
 			fill32_px(pt, vm->scratch[0].encode);
 
 			spin_lock(&pd->lock);
 			if (pd->entry[pde] == &vm->scratch[1]) {
+				stash->pt[0] = pt->stash;
+				atomic_set(&pt->used, 0);
 				pd->entry[pde] = pt;
 			} else {
-				alloc = pt;
 				pt = pd->entry[pde];
 			}
 		}
@@ -223,15 +219,7 @@ static int gen6_alloc_va_range(struct i915_address_space *vm,
 	if (i915_vma_is_bound(ppgtt->vma, I915_VMA_GLOBAL_BIND))
 		gen6_flush_pd(ppgtt, from, start);
 
-	goto out;
-
-unwind_out:
-	gen6_ppgtt_clear_range(vm, from, start - from);
-out:
-	if (alloc)
-		free_px(vm, alloc);
 	intel_runtime_pm_put(&vm->i915->runtime_pm, wakeref);
-	return ret;
 }
 
 static int gen6_ppgtt_init_scratch(struct gen6_ppgtt *ppgtt)
@@ -299,10 +287,11 @@ static void pd_vma_clear_pages(struct i915_vma *vma)
 	vma->pages = NULL;
 }
 
-static int pd_vma_bind(struct i915_address_space *vm,
-		       struct i915_vma *vma,
-		       enum i915_cache_level cache_level,
-		       u32 unused)
+static void pd_vma_bind(struct i915_address_space *vm,
+			struct i915_vm_pt_stash *stash,
+			struct i915_vma *vma,
+			enum i915_cache_level cache_level,
+			u32 unused)
 {
 	struct i915_ggtt *ggtt = i915_vm_to_ggtt(vm);
 	struct gen6_ppgtt *ppgtt = vma->private;
@@ -312,7 +301,6 @@ static int pd_vma_bind(struct i915_address_space *vm,
 	ppgtt->pd_addr = (gen6_pte_t __iomem *)ggtt->gsm + ggtt_offset;
 
 	gen6_flush_pd(ppgtt, 0, ppgtt->base.vm.total);
-	return 0;
 }
 
 static void pd_vma_unbind(struct i915_address_space *vm, struct i915_vma *vma)
diff --git a/drivers/gpu/drm/i915/gt/gen8_ppgtt.c b/drivers/gpu/drm/i915/gt/gen8_ppgtt.c
index 699125928272..e6f2acd445dd 100644
--- a/drivers/gpu/drm/i915/gt/gen8_ppgtt.c
+++ b/drivers/gpu/drm/i915/gt/gen8_ppgtt.c
@@ -269,14 +269,12 @@ static void gen8_ppgtt_clear(struct i915_address_space *vm,
 			   start, start + length, vm->top);
 }
 
-static int __gen8_ppgtt_alloc(struct i915_address_space * const vm,
-			      struct i915_page_directory * const pd,
-			      u64 * const start, const u64 end, int lvl)
+static void __gen8_ppgtt_alloc(struct i915_address_space * const vm,
+			       struct i915_vm_pt_stash *stash,
+			       struct i915_page_directory * const pd,
+			       u64 * const start, const u64 end, int lvl)
 {
-	const struct i915_page_scratch * const scratch = &vm->scratch[lvl];
-	struct i915_page_table *alloc = NULL;
 	unsigned int idx, len;
-	int ret = 0;
 
 	GEM_BUG_ON(end > vm->total >> GEN8_PTE_SHIFT);
 
@@ -297,49 +295,30 @@ static int __gen8_ppgtt_alloc(struct i915_address_space * const vm,
 			DBG("%s(%p):{ lvl:%d, idx:%d } allocating new tree\n",
 			    __func__, vm, lvl + 1, idx);
 
-			pt = fetch_and_zero(&alloc);
-			if (lvl) {
-				if (!pt) {
-					pt = &alloc_pd(vm)->pt;
-					if (IS_ERR(pt)) {
-						ret = PTR_ERR(pt);
-						goto out;
-					}
-				}
+			pt = stash->pt[!!lvl];
+			GEM_BUG_ON(!pt);
 
+			if (lvl ||
+			    gen8_pt_count(*start, end) < I915_PDES ||
+			    intel_vgpu_active(vm->i915))
 				fill_px(pt, vm->scratch[lvl].encode);
-			} else {
-				if (!pt) {
-					pt = alloc_pt(vm);
-					if (IS_ERR(pt)) {
-						ret = PTR_ERR(pt);
-						goto out;
-					}
-				}
-
-				if (intel_vgpu_active(vm->i915) ||
-				    gen8_pt_count(*start, end) < I915_PDES)
-					fill_px(pt, vm->scratch[lvl].encode);
-			}
 
 			spin_lock(&pd->lock);
-			if (likely(!pd->entry[idx]))
+			if (likely(!pd->entry[idx])) {
+				stash->pt[!!lvl] = pt->stash;
+				atomic_set(&pt->used, 0);
 				set_pd_entry(pd, idx, pt);
-			else
-				alloc = pt, pt = pd->entry[idx];
+			} else {
+				pt = pd->entry[idx];
+			}
 		}
 
 		if (lvl) {
 			atomic_inc(&pt->used);
 			spin_unlock(&pd->lock);
 
-			ret = __gen8_ppgtt_alloc(vm, as_pd(pt),
-						 start, end, lvl);
-			if (unlikely(ret)) {
-				if (release_pd_entry(pd, idx, pt, scratch))
-					free_px(vm, pt);
-				goto out;
-			}
+			__gen8_ppgtt_alloc(vm, stash,
+					   as_pd(pt), start, end, lvl);
 
 			spin_lock(&pd->lock);
 			atomic_dec(&pt->used);
@@ -359,18 +338,12 @@ static int __gen8_ppgtt_alloc(struct i915_address_space * const vm,
 		}
 	} while (idx++, --len);
 	spin_unlock(&pd->lock);
-out:
-	if (alloc)
-		free_px(vm, alloc);
-	return ret;
 }
 
-static int gen8_ppgtt_alloc(struct i915_address_space *vm,
-			    u64 start, u64 length)
+static void gen8_ppgtt_alloc(struct i915_address_space *vm,
+			     struct i915_vm_pt_stash *stash,
+			     u64 start, u64 length)
 {
-	u64 from;
-	int err;
-
 	GEM_BUG_ON(!IS_ALIGNED(start, BIT_ULL(GEN8_PTE_SHIFT)));
 	GEM_BUG_ON(!IS_ALIGNED(length, BIT_ULL(GEN8_PTE_SHIFT)));
 	GEM_BUG_ON(range_overflows(start, length, vm->total));
@@ -378,15 +351,9 @@ static int gen8_ppgtt_alloc(struct i915_address_space *vm,
 	start >>= GEN8_PTE_SHIFT;
 	length >>= GEN8_PTE_SHIFT;
 	GEM_BUG_ON(length == 0);
-	from = start;
-
-	err = __gen8_ppgtt_alloc(vm, i915_vm_to_ppgtt(vm)->pd,
-				 &start, start + length, vm->top);
-	if (unlikely(err && from != start))
-		__gen8_ppgtt_clear(vm, i915_vm_to_ppgtt(vm)->pd,
-				   from, start, vm->top);
 
-	return err;
+	__gen8_ppgtt_alloc(vm, stash, i915_vm_to_ppgtt(vm)->pd,
+			   &start, start + length, vm->top);
 }
 
 static __always_inline void
diff --git a/drivers/gpu/drm/i915/gt/intel_ggtt.c b/drivers/gpu/drm/i915/gt/intel_ggtt.c
index 62979ea591f0..5a33056ab976 100644
--- a/drivers/gpu/drm/i915/gt/intel_ggtt.c
+++ b/drivers/gpu/drm/i915/gt/intel_ggtt.c
@@ -436,16 +436,17 @@ static void i915_ggtt_clear_range(struct i915_address_space *vm,
 	intel_gtt_clear_range(start >> PAGE_SHIFT, length >> PAGE_SHIFT);
 }
 
-static int ggtt_bind_vma(struct i915_address_space *vm,
-			 struct i915_vma *vma,
-			 enum i915_cache_level cache_level,
-			 u32 flags)
+static void ggtt_bind_vma(struct i915_address_space *vm,
+			  struct i915_vm_pt_stash *stash,
+			  struct i915_vma *vma,
+			  enum i915_cache_level cache_level,
+			  u32 flags)
 {
 	struct drm_i915_gem_object *obj = vma->obj;
 	u32 pte_flags;
 
 	if (i915_vma_is_bound(vma, ~flags & I915_VMA_BIND_MASK))
-		return 0;
+		return;
 
 	/* Applicable to VLV (gen8+ do not support RO in the GGTT) */
 	pte_flags = 0;
@@ -454,8 +455,6 @@ static int ggtt_bind_vma(struct i915_address_space *vm,
 
 	vm->insert_entries(vm, vma, cache_level, pte_flags);
 	vma->page_sizes.gtt = I915_GTT_PAGE_SIZE;
-
-	return 0;
 }
 
 static void ggtt_unbind_vma(struct i915_address_space *vm, struct i915_vma *vma)
@@ -568,31 +567,25 @@ static int init_ggtt(struct i915_ggtt *ggtt)
 	return ret;
 }
 
-static int aliasing_gtt_bind_vma(struct i915_address_space *vm,
-				 struct i915_vma *vma,
-				 enum i915_cache_level cache_level,
-				 u32 flags)
+static void aliasing_gtt_bind_vma(struct i915_address_space *vm,
+				  struct i915_vm_pt_stash *stash,
+				  struct i915_vma *vma,
+				  enum i915_cache_level cache_level,
+				  u32 flags)
 {
 	u32 pte_flags;
-	int ret;
 
 	/* Currently applicable only to VLV */
 	pte_flags = 0;
 	if (i915_gem_object_is_readonly(vma->obj))
 		pte_flags |= PTE_READ_ONLY;
 
-	if (flags & I915_VMA_LOCAL_BIND) {
-		struct i915_ppgtt *alias = i915_vm_to_ggtt(vm)->alias;
-
-		ret = ppgtt_bind_vma(&alias->vm, vma, cache_level, flags);
-		if (ret)
-			return ret;
-	}
+	if (flags & I915_VMA_LOCAL_BIND)
+		ppgtt_bind_vma(&i915_vm_to_ggtt(vm)->alias->vm,
+			       stash, vma, cache_level, flags);
 
 	if (flags & I915_VMA_GLOBAL_BIND)
 		vm->insert_entries(vm, vma, cache_level, pte_flags);
-
-	return 0;
 }
 
 static void aliasing_gtt_unbind_vma(struct i915_address_space *vm,
@@ -607,6 +600,7 @@ static void aliasing_gtt_unbind_vma(struct i915_address_space *vm,
 
 static int init_aliasing_ppgtt(struct i915_ggtt *ggtt)
 {
+	struct i915_vm_pt_stash stash = {};
 	struct i915_ppgtt *ppgtt;
 	int err;
 
@@ -619,15 +613,17 @@ static int init_aliasing_ppgtt(struct i915_ggtt *ggtt)
 		goto err_ppgtt;
 	}
 
+	err = i915_vm_alloc_pt_stash(&ppgtt->vm, &stash, ggtt->vm.total);
+	if (err)
+		goto err_ppgtt;
+
 	/*
 	 * Note we only pre-allocate as far as the end of the global
 	 * GTT. On 48b / 4-level page-tables, the difference is very,
 	 * very significant! We have to preallocate as GVT/vgpu does
 	 * not like the page directory disappearing.
 	 */
-	err = ppgtt->vm.allocate_va_range(&ppgtt->vm, 0, ggtt->vm.total);
-	if (err)
-		goto err_ppgtt;
+	ppgtt->vm.allocate_va_range(&ppgtt->vm, &stash, 0, ggtt->vm.total);
 
 	ggtt->alias = ppgtt;
 	ggtt->vm.bind_async_flags |= ppgtt->vm.bind_async_flags;
@@ -638,6 +634,7 @@ static int init_aliasing_ppgtt(struct i915_ggtt *ggtt)
 	GEM_BUG_ON(ggtt->vm.vma_ops.unbind_vma != ggtt_unbind_vma);
 	ggtt->vm.vma_ops.unbind_vma = aliasing_gtt_unbind_vma;
 
+	i915_vm_free_pt_stash(&ppgtt->vm, &stash);
 	return 0;
 
 err_ppgtt:
@@ -1165,11 +1162,6 @@ void i915_ggtt_disable_guc(struct i915_ggtt *ggtt)
 	ggtt->invalidate(ggtt);
 }
 
-static unsigned int clear_bind(struct i915_vma *vma)
-{
-	return atomic_fetch_and(~I915_VMA_BIND_MASK, &vma->flags);
-}
-
 void i915_ggtt_resume(struct i915_ggtt *ggtt)
 {
 	struct i915_vma *vma;
@@ -1187,11 +1179,13 @@ void i915_ggtt_resume(struct i915_ggtt *ggtt)
 	/* clflush objects bound into the GGTT and rebind them. */
 	list_for_each_entry(vma, &ggtt->vm.bound_list, vm_link) {
 		struct drm_i915_gem_object *obj = vma->obj;
-		unsigned int was_bound = clear_bind(vma);
+		unsigned int was_bound =
+			atomic_read(&vma->flags) & I915_VMA_BIND_MASK;
 
-		WARN_ON(i915_vma_bind(vma,
-				      obj ? obj->cache_level : 0,
-				      was_bound, NULL));
+		GEM_BUG_ON(!was_bound);
+		vma->ops->bind_vma(&ggtt->vm, NULL, vma,
+				   obj ? obj->cache_level : 0,
+				   was_bound);
 		if (obj) { /* only used during resume => exclusive access */
 			flush |= fetch_and_zero(&obj->write_domain);
 			obj->read_domains |= I915_GEM_DOMAIN_GTT;
diff --git a/drivers/gpu/drm/i915/gt/intel_gtt.h b/drivers/gpu/drm/i915/gt/intel_gtt.h
index f2b75078e05f..8bd462d2fcd9 100644
--- a/drivers/gpu/drm/i915/gt/intel_gtt.h
+++ b/drivers/gpu/drm/i915/gt/intel_gtt.h
@@ -159,7 +159,10 @@ struct i915_page_scratch {
 
 struct i915_page_table {
 	struct i915_page_dma base;
-	atomic_t used;
+	union {
+		atomic_t used;
+		struct i915_page_table *stash;
+	};
 };
 
 struct i915_page_directory {
@@ -196,12 +199,18 @@ struct drm_i915_gem_object;
 struct i915_vma;
 struct intel_gt;
 
+struct i915_vm_pt_stash {
+	/* preallocated chains of page tables/directories */
+	struct i915_page_table *pt[2];
+};
+
 struct i915_vma_ops {
 	/* Map an object into an address space with the given cache flags. */
-	int (*bind_vma)(struct i915_address_space *vm,
-			struct i915_vma *vma,
-			enum i915_cache_level cache_level,
-			u32 flags);
+	void (*bind_vma)(struct i915_address_space *vm,
+			 struct i915_vm_pt_stash *stash,
+			 struct i915_vma *vma,
+			 enum i915_cache_level cache_level,
+			 u32 flags);
 	/*
 	 * Unmap an object from an address space. This usually consists of
 	 * setting the valid PTE entries to a reserved scratch page.
@@ -281,8 +290,9 @@ struct i915_address_space {
 			  u32 flags); /* Create a valid PTE */
 #define PTE_READ_ONLY	BIT(0)
 
-	int (*allocate_va_range)(struct i915_address_space *vm,
-				 u64 start, u64 length);
+	void (*allocate_va_range)(struct i915_address_space *vm,
+				  struct i915_vm_pt_stash *stash,
+				  u64 start, u64 length);
 	void (*clear_range)(struct i915_address_space *vm,
 			    u64 start, u64 length);
 	void (*insert_page)(struct i915_address_space *vm,
@@ -568,10 +578,11 @@ int ggtt_set_pages(struct i915_vma *vma);
 int ppgtt_set_pages(struct i915_vma *vma);
 void clear_pages(struct i915_vma *vma);
 
-int ppgtt_bind_vma(struct i915_address_space *vm,
-		   struct i915_vma *vma,
-		   enum i915_cache_level cache_level,
-		   u32 flags);
+void ppgtt_bind_vma(struct i915_address_space *vm,
+		    struct i915_vm_pt_stash *stash,
+		    struct i915_vma *vma,
+		    enum i915_cache_level cache_level,
+		    u32 flags);
 void ppgtt_unbind_vma(struct i915_address_space *vm,
 		      struct i915_vma *vma);
 
@@ -579,6 +590,12 @@ void gtt_write_workarounds(struct intel_gt *gt);
 
 void setup_private_pat(struct intel_uncore *uncore);
 
+int i915_vm_alloc_pt_stash(struct i915_address_space *vm,
+			   struct i915_vm_pt_stash *stash,
+			   u64 size);
+void i915_vm_free_pt_stash(struct i915_address_space *vm,
+			   struct i915_vm_pt_stash *stash);
+
 static inline struct sgt_dma {
 	struct scatterlist *sg;
 	dma_addr_t dma, max;
diff --git a/drivers/gpu/drm/i915/gt/intel_ppgtt.c b/drivers/gpu/drm/i915/gt/intel_ppgtt.c
index f0862e924d11..9633fd2d294d 100644
--- a/drivers/gpu/drm/i915/gt/intel_ppgtt.c
+++ b/drivers/gpu/drm/i915/gt/intel_ppgtt.c
@@ -155,19 +155,16 @@ struct i915_ppgtt *i915_ppgtt_create(struct intel_gt *gt)
 	return ppgtt;
 }
 
-int ppgtt_bind_vma(struct i915_address_space *vm,
-		   struct i915_vma *vma,
-		   enum i915_cache_level cache_level,
-		   u32 flags)
+void ppgtt_bind_vma(struct i915_address_space *vm,
+		    struct i915_vm_pt_stash *stash,
+		    struct i915_vma *vma,
+		    enum i915_cache_level cache_level,
+		    u32 flags)
 {
 	u32 pte_flags;
-	int err;
 
 	if (!test_bit(I915_VMA_ALLOC_BIT, __i915_vma_flags(vma))) {
-		err = vm->allocate_va_range(vm, vma->node.start, vma->size);
-		if (err)
-			return err;
-
+		vm->allocate_va_range(vm, stash, vma->node.start, vma->size);
 		set_bit(I915_VMA_ALLOC_BIT, __i915_vma_flags(vma));
 	}
 
@@ -178,8 +175,6 @@ int ppgtt_bind_vma(struct i915_address_space *vm,
 
 	vm->insert_entries(vm, vma, cache_level, pte_flags);
 	wmb();
-
-	return 0;
 }
 
 void ppgtt_unbind_vma(struct i915_address_space *vm, struct i915_vma *vma)
@@ -188,12 +183,73 @@ void ppgtt_unbind_vma(struct i915_address_space *vm, struct i915_vma *vma)
 		vm->clear_range(vm, vma->node.start, vma->size);
 }
 
+static unsigned long pd_count(u64 size, int shift)
+{
+	/* Beware later misalignment */
+	return (size + 2 * (BIT_ULL(shift) - 1)) >> shift;
+}
+
+int i915_vm_alloc_pt_stash(struct i915_address_space *vm,
+			   struct i915_vm_pt_stash *stash,
+			   u64 size)
+{
+	unsigned long count;
+	int shift = 21;
+	int n;
+
+	count = pd_count(size, shift);
+	while (count--) {
+		struct i915_page_table *pt;
+
+		pt = alloc_pt(vm);
+		if (IS_ERR(pt)) {
+			i915_vm_free_pt_stash(vm, stash);
+			return PTR_ERR(pt);
+		}
+
+		pt->stash = stash->pt[0];
+		stash->pt[0] = pt;
+	}
+
+	for (n = 1; n < vm->top; n++) {
+		shift += 9;
+		count = pd_count(size, shift);
+		while (count--) {
+			struct i915_page_directory *pd;
+
+			pd = alloc_pd(vm);
+			if (IS_ERR(pd)) {
+				i915_vm_free_pt_stash(vm, stash);
+				return PTR_ERR(pd);
+			}
+
+			pd->pt.stash = stash->pt[1];
+			stash->pt[1] = &pd->pt;
+		}
+	}
+
+	return 0;
+}
+
+void i915_vm_free_pt_stash(struct i915_address_space *vm,
+			   struct i915_vm_pt_stash *stash)
+{
+	struct i915_page_table *pt;
+	int n;
+
+	for (n = 0; n < ARRAY_SIZE(stash->pt); n++) {
+		while ((pt = stash->pt[n])) {
+			stash->pt[n] = pt->stash;
+			free_px(vm, pt);
+		}
+	}
+}
+
 int ppgtt_set_pages(struct i915_vma *vma)
 {
 	GEM_BUG_ON(vma->pages);
 
 	vma->pages = vma->obj->mm.pages;
-
 	vma->page_sizes = vma->obj->mm.page_sizes;
 
 	return 0;
diff --git a/drivers/gpu/drm/i915/i915_vma.c b/drivers/gpu/drm/i915/i915_vma.c
index bc64f773dcdb..d15a8e7d514a 100644
--- a/drivers/gpu/drm/i915/i915_vma.c
+++ b/drivers/gpu/drm/i915/i915_vma.c
@@ -291,6 +291,8 @@ i915_vma_instance(struct drm_i915_gem_object *obj,
 
 struct i915_vma_work {
 	struct dma_fence_work base;
+	struct i915_address_space *vm;
+	struct i915_vm_pt_stash stash;
 	struct i915_vma *vma;
 	struct drm_i915_gem_object *pinned;
 	struct i915_sw_dma_fence_cb cb;
@@ -302,13 +304,10 @@ 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->vm, vma, vw->cache_level, vw->flags);
-	if (err)
-		atomic_or(I915_VMA_ERROR, &vma->flags);
 
-	return err;
+	vma->ops->bind_vma(vw->vm, &vw->stash,
+			   vma, vw->cache_level, vw->flags);
+	return 0;
 }
 
 static void __vma_release(struct dma_fence_work *work)
@@ -317,6 +316,9 @@ static void __vma_release(struct dma_fence_work *work)
 
 	if (vw->pinned)
 		__i915_gem_object_unpin_pages(vw->pinned);
+
+	i915_vm_free_pt_stash(vw->vm, &vw->stash);
+	i915_vm_put(vw->vm);
 }
 
 static const struct dma_fence_work_ops bind_ops = {
@@ -376,7 +378,6 @@ int i915_vma_bind(struct i915_vma *vma,
 {
 	u32 bind_flags;
 	u32 vma_flags;
-	int ret;
 
 	GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
 	GEM_BUG_ON(vma->size > vma->node.size);
@@ -433,9 +434,7 @@ int i915_vma_bind(struct i915_vma *vma,
 			work->pinned = vma->obj;
 		}
 	} else {
-		ret = vma->ops->bind_vma(vma->vm, vma, cache_level, bind_flags);
-		if (ret)
-			return ret;
+		vma->ops->bind_vma(vma->vm, NULL, vma, cache_level, bind_flags);
 	}
 
 	atomic_or(bind_flags, &vma->flags);
@@ -874,11 +873,21 @@ int i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
 		return err;
 
 	if (flags & vma->vm->bind_async_flags) {
+		u64 max_size;
+
 		work = i915_vma_work();
 		if (!work) {
 			err = -ENOMEM;
 			goto err_pages;
 		}
+
+		work->vm = i915_vm_get(vma->vm);
+
+		/* Allocate enough page directories to cover worst case */
+		max_size = max(size, vma->size);
+		if (flags & PIN_MAPPABLE)
+			max_size = max_t(u64, max_size, vma->fence_size);
+		i915_vm_alloc_pt_stash(vma->vm, &work->stash, max_size);
 	}
 
 	if (flags & PIN_GLOBAL)
diff --git a/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c b/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
index 0016ffc7d914..9b8fc990e9ef 100644
--- a/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
+++ b/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
@@ -172,35 +172,33 @@ static int igt_ppgtt_alloc(void *arg)
 
 	/* Check we can allocate the entire range */
 	for (size = 4096; size <= limit; size <<= 2) {
-		err = ppgtt->vm.allocate_va_range(&ppgtt->vm, 0, size);
-		if (err) {
-			if (err == -ENOMEM) {
-				pr_info("[1] Ran out of memory for va_range [0 + %llx] [bit %d]\n",
-					size, ilog2(size));
-				err = 0; /* virtual space too large! */
-			}
+		struct i915_vm_pt_stash stash = {};
+
+		err = i915_vm_alloc_pt_stash(&ppgtt->vm, &stash, size);
+		if (err)
 			goto err_ppgtt_cleanup;
-		}
 
+		ppgtt->vm.allocate_va_range(&ppgtt->vm, &stash, 0, size);
 		cond_resched();
 
 		ppgtt->vm.clear_range(&ppgtt->vm, 0, size);
+
+		i915_vm_free_pt_stash(&ppgtt->vm, &stash);
 	}
 
 	/* Check we can incrementally allocate the entire range */
 	for (last = 0, size = 4096; size <= limit; last = size, size <<= 2) {
-		err = ppgtt->vm.allocate_va_range(&ppgtt->vm,
-						  last, size - last);
-		if (err) {
-			if (err == -ENOMEM) {
-				pr_info("[2] Ran out of memory for va_range [%llx + %llx] [bit %d]\n",
-					last, size - last, ilog2(size));
-				err = 0; /* virtual space too large! */
-			}
+		struct i915_vm_pt_stash stash = {};
+
+		err = i915_vm_alloc_pt_stash(&ppgtt->vm, &stash, size - last);
+		if (err)
 			goto err_ppgtt_cleanup;
-		}
 
+		ppgtt->vm.allocate_va_range(&ppgtt->vm, &stash,
+					    last, size - last);
 		cond_resched();
+
+		i915_vm_free_pt_stash(&ppgtt->vm, &stash);
 	}
 
 err_ppgtt_cleanup:
@@ -284,9 +282,18 @@ static int lowlevel_hole(struct i915_address_space *vm,
 				break;
 			}
 
-			if (vm->allocate_va_range &&
-			    vm->allocate_va_range(vm, addr, BIT_ULL(size)))
-				break;
+			if (vm->allocate_va_range) {
+				struct i915_vm_pt_stash stash = {};
+
+				if (i915_vm_alloc_pt_stash(vm, &stash,
+							   BIT_ULL(size)))
+					break;
+
+				vm->allocate_va_range(vm, &stash,
+						      addr, BIT_ULL(size));
+
+				i915_vm_free_pt_stash(vm, &stash);
+			}
 
 			mock_vma->pages = obj->mm.pages;
 			mock_vma->node.size = BIT_ULL(size);
@@ -1881,6 +1888,7 @@ static int igt_cs_tlb(void *arg)
 			continue;
 
 		while (!__igt_timeout(end_time, NULL)) {
+			struct i915_vm_pt_stash stash = {};
 			struct i915_request *rq;
 			u64 offset;
 
@@ -1888,10 +1896,6 @@ static int igt_cs_tlb(void *arg)
 						   0, vm->total - PAGE_SIZE,
 						   chunk_size, PAGE_SIZE);
 
-			err = vm->allocate_va_range(vm, offset, chunk_size);
-			if (err)
-				goto end;
-
 			memset32(result, STACK_MAGIC, PAGE_SIZE / sizeof(u32));
 
 			vma = i915_vma_instance(bbe, vm, NULL);
@@ -1904,6 +1908,14 @@ static int igt_cs_tlb(void *arg)
 			if (err)
 				goto end;
 
+			err = i915_vm_alloc_pt_stash(vm, &stash, chunk_size);
+			if (err)
+				goto end;
+
+			vm->allocate_va_range(vm, &stash, offset, chunk_size);
+
+			i915_vm_free_pt_stash(vm, &stash);
+
 			/* Prime the TLB with the dummy pages */
 			for (i = 0; i < count; i++) {
 				vma->node.start = offset + i * PAGE_SIZE;
diff --git a/drivers/gpu/drm/i915/selftests/mock_gtt.c b/drivers/gpu/drm/i915/selftests/mock_gtt.c
index b173086411ef..5e4fb0fba34b 100644
--- a/drivers/gpu/drm/i915/selftests/mock_gtt.c
+++ b/drivers/gpu/drm/i915/selftests/mock_gtt.c
@@ -38,14 +38,14 @@ static void mock_insert_entries(struct i915_address_space *vm,
 {
 }
 
-static int mock_bind_ppgtt(struct i915_address_space *vm,
-			   struct i915_vma *vma,
-			   enum i915_cache_level cache_level,
-			   u32 flags)
+static void mock_bind_ppgtt(struct i915_address_space *vm,
+			    struct i915_vm_pt_stash *stash,
+			    struct i915_vma *vma,
+			    enum i915_cache_level cache_level,
+			    u32 flags)
 {
 	GEM_BUG_ON(flags & I915_VMA_GLOBAL_BIND);
 	set_bit(I915_VMA_LOCAL_BIND_BIT, __i915_vma_flags(vma));
-	return 0;
 }
 
 static void mock_unbind_ppgtt(struct i915_address_space *vm,
@@ -74,6 +74,7 @@ struct i915_ppgtt *mock_ppgtt(struct drm_i915_private *i915, const char *name)
 	ppgtt->vm.i915 = i915;
 	ppgtt->vm.total = round_down(U64_MAX, PAGE_SIZE);
 	ppgtt->vm.file = ERR_PTR(-ENODEV);
+	ppgtt->vm.dma = &i915->drm.pdev->dev;
 
 	i915_address_space_init(&ppgtt->vm, VM_CLASS_PPGTT);
 
@@ -90,13 +91,12 @@ struct i915_ppgtt *mock_ppgtt(struct drm_i915_private *i915, const char *name)
 	return ppgtt;
 }
 
-static int mock_bind_ggtt(struct i915_address_space *vm,
-			  struct i915_vma *vma,
-			  enum i915_cache_level cache_level,
-			  u32 flags)
+static void mock_bind_ggtt(struct i915_address_space *vm,
+			   struct i915_vm_pt_stash *stash,
+			   struct i915_vma *vma,
+			   enum i915_cache_level cache_level,
+			   u32 flags)
 {
-	atomic_or(I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND, &vma->flags);
-	return 0;
 }
 
 static void mock_unbind_ggtt(struct i915_address_space *vm,
-- 
2.20.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [Intel-gfx] [PATCH 02/20] drm/i915: Switch to object allocations for page directories
  2020-07-06  6:19 [Intel-gfx] s/obj->mm.lock// Chris Wilson
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 01/20] drm/i915: Preallocate stashes for vma page-directories Chris Wilson
@ 2020-07-06  6:19 ` Chris Wilson
  2020-07-06 19:06   ` Matthew Auld
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 03/20] drm/i915/gem: Don't drop the timeline lock during execbuf Chris Wilson
                   ` (22 subsequent siblings)
  24 siblings, 1 reply; 65+ messages in thread
From: Chris Wilson @ 2020-07-06  6:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: Chris Wilson

The GEM object is grossly overweight for the practicality of tracking
large numbers of individual pages, yet it is currently our only
abstraction for tracking DMA allocations. Since those allocations need
to be reserved upfront before an operation, and that we need to break
away from simple system memory, we need to ditch using plain struct page
wrappers.

In the process, we drop the WC mapping as we ended up clflushing
everything anyway due to various issues across a wider range of
platforms. Though in a future step, we need to drop the kmap_atomic
approach which suggests we need to pre-map all the pages and keep them
mapped.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 .../gpu/drm/i915/gem/i915_gem_object_types.h  |   1 +
 .../gpu/drm/i915/gem/selftests/huge_pages.c   |   2 +-
 .../drm/i915/gem/selftests/i915_gem_context.c |   2 +-
 drivers/gpu/drm/i915/gt/gen6_ppgtt.c          |  53 ++--
 drivers/gpu/drm/i915/gt/gen6_ppgtt.h          |   1 +
 drivers/gpu/drm/i915/gt/gen8_ppgtt.c          |  89 +++---
 drivers/gpu/drm/i915/gt/intel_ggtt.c          |  37 ++-
 drivers/gpu/drm/i915/gt/intel_gtt.c           | 292 +++---------------
 drivers/gpu/drm/i915/gt/intel_gtt.h           |  94 ++----
 drivers/gpu/drm/i915/gt/intel_ppgtt.c         |  42 ++-
 .../gpu/drm/i915/gt/intel_ring_submission.c   |  16 +-
 drivers/gpu/drm/i915/gvt/scheduler.c          |  17 +-
 drivers/gpu/drm/i915/i915_drv.c               |   1 +
 drivers/gpu/drm/i915/i915_drv.h               |   5 -
 drivers/gpu/drm/i915/i915_vma.c               |  18 +-
 drivers/gpu/drm/i915/selftests/i915_gem_gtt.c |  23 ++
 drivers/gpu/drm/i915/selftests/mock_gtt.c     |   4 +
 17 files changed, 278 insertions(+), 419 deletions(-)

diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object_types.h b/drivers/gpu/drm/i915/gem/i915_gem_object_types.h
index 5335f799b548..d0847d7896f9 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_object_types.h
+++ b/drivers/gpu/drm/i915/gem/i915_gem_object_types.h
@@ -282,6 +282,7 @@ struct drm_i915_gem_object {
 		} userptr;
 
 		unsigned long scratch;
+		u64 encode;
 
 		void *gvt_info;
 	};
diff --git a/drivers/gpu/drm/i915/gem/selftests/huge_pages.c b/drivers/gpu/drm/i915/gem/selftests/huge_pages.c
index 8291ede6902c..9fb06fcc8f8f 100644
--- a/drivers/gpu/drm/i915/gem/selftests/huge_pages.c
+++ b/drivers/gpu/drm/i915/gem/selftests/huge_pages.c
@@ -393,7 +393,7 @@ static int igt_mock_exhaust_device_supported_pages(void *arg)
 	 */
 
 	for (i = 1; i < BIT(ARRAY_SIZE(page_sizes)); i++) {
-		unsigned int combination = 0;
+		unsigned int combination = SZ_4K;
 
 		for (j = 0; j < ARRAY_SIZE(page_sizes); j++) {
 			if (i & BIT(j))
diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c
index b81978890641..1308198543d8 100644
--- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c
+++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c
@@ -1745,7 +1745,7 @@ static int check_scratch_page(struct i915_gem_context *ctx, u32 *out)
 	if (!vm)
 		return -ENODEV;
 
-	page = vm->scratch[0].base.page;
+	page = __px_page(vm->scratch[0]);
 	if (!page) {
 		pr_err("No scratch page!\n");
 		return -EINVAL;
diff --git a/drivers/gpu/drm/i915/gt/gen6_ppgtt.c b/drivers/gpu/drm/i915/gt/gen6_ppgtt.c
index 35e2b698f9ed..1c1e807f674d 100644
--- a/drivers/gpu/drm/i915/gt/gen6_ppgtt.c
+++ b/drivers/gpu/drm/i915/gt/gen6_ppgtt.c
@@ -16,8 +16,10 @@ static inline void gen6_write_pde(const struct gen6_ppgtt *ppgtt,
 				  const unsigned int pde,
 				  const struct i915_page_table *pt)
 {
+	dma_addr_t addr = pt ? px_dma(pt) : px_dma(ppgtt->base.vm.scratch[1]);
+
 	/* Caller needs to make sure the write completes if necessary */
-	iowrite32(GEN6_PDE_ADDR_ENCODE(px_dma(pt)) | GEN6_PDE_VALID,
+	iowrite32(GEN6_PDE_ADDR_ENCODE(addr) | GEN6_PDE_VALID,
 		  ppgtt->pd_addr + pde);
 }
 
@@ -79,7 +81,7 @@ static void gen6_ppgtt_clear_range(struct i915_address_space *vm,
 {
 	struct gen6_ppgtt * const ppgtt = to_gen6_ppgtt(i915_vm_to_ppgtt(vm));
 	const unsigned int first_entry = start / I915_GTT_PAGE_SIZE;
-	const gen6_pte_t scratch_pte = vm->scratch[0].encode;
+	const gen6_pte_t scratch_pte = vm->scratch[0]->encode;
 	unsigned int pde = first_entry / GEN6_PTES;
 	unsigned int pte = first_entry % GEN6_PTES;
 	unsigned int num_entries = length / I915_GTT_PAGE_SIZE;
@@ -90,8 +92,6 @@ static void gen6_ppgtt_clear_range(struct i915_address_space *vm,
 		const unsigned int count = min(num_entries, GEN6_PTES - pte);
 		gen6_pte_t *vaddr;
 
-		GEM_BUG_ON(px_base(pt) == px_base(&vm->scratch[1]));
-
 		num_entries -= count;
 
 		GEM_BUG_ON(count > atomic_read(&pt->used));
@@ -127,7 +127,7 @@ static void gen6_ppgtt_insert_entries(struct i915_address_space *vm,
 	struct sgt_dma iter = sgt_dma(vma);
 	gen6_pte_t *vaddr;
 
-	GEM_BUG_ON(pd->entry[act_pt] == &vm->scratch[1]);
+	GEM_BUG_ON(!pd->entry[act_pt]);
 
 	vaddr = kmap_atomic_px(i915_pt_entry(pd, act_pt));
 	do {
@@ -194,16 +194,17 @@ static void gen6_alloc_va_range(struct i915_address_space *vm,
 	gen6_for_each_pde(pt, pd, start, length, pde) {
 		const unsigned int count = gen6_pte_count(start, length);
 
-		if (px_base(pt) == px_base(&vm->scratch[1])) {
+		if (!pt) {
 			spin_unlock(&pd->lock);
 
 			pt = stash->pt[0];
-			GEM_BUG_ON(!pt);
+			__i915_gem_object_pin_pages(pt->base);
+			i915_gem_object_make_unshrinkable(pt->base);
 
-			fill32_px(pt, vm->scratch[0].encode);
+			fill32_px(pt, vm->scratch[0]->encode);
 
 			spin_lock(&pd->lock);
-			if (pd->entry[pde] == &vm->scratch[1]) {
+			if (!pd->entry[pde]) {
 				stash->pt[0] = pt->stash;
 				atomic_set(&pt->used, 0);
 				pd->entry[pde] = pt;
@@ -225,24 +226,27 @@ static void gen6_alloc_va_range(struct i915_address_space *vm,
 static int gen6_ppgtt_init_scratch(struct gen6_ppgtt *ppgtt)
 {
 	struct i915_address_space * const vm = &ppgtt->base.vm;
-	struct i915_page_directory * const pd = ppgtt->base.pd;
 	int ret;
 
-	ret = setup_scratch_page(vm, __GFP_HIGHMEM);
+	ret = setup_scratch_page(vm);
 	if (ret)
 		return ret;
 
-	vm->scratch[0].encode =
-		vm->pte_encode(px_dma(&vm->scratch[0]),
+	vm->scratch[0]->encode =
+		vm->pte_encode(px_dma(vm->scratch[0]),
 			       I915_CACHE_NONE, PTE_READ_ONLY);
 
-	if (unlikely(setup_page_dma(vm, px_base(&vm->scratch[1])))) {
-		cleanup_scratch_page(vm);
-		return -ENOMEM;
+	vm->scratch[1] = vm->alloc_pt_dma(vm, I915_GTT_PAGE_SIZE_4K);
+	if (IS_ERR(vm->scratch[1]))
+		return PTR_ERR(vm->scratch[1]);
+
+	ret = pin_pt_dma(vm, vm->scratch[1]);
+	if (ret) {
+		i915_gem_object_put(vm->scratch[1]);
+		return ret;
 	}
 
-	fill32_px(&vm->scratch[1], vm->scratch[0].encode);
-	memset_p(pd->entry, &vm->scratch[1], I915_PDES);
+	fill32_px(vm->scratch[1], vm->scratch[0]->encode);
 
 	return 0;
 }
@@ -250,13 +254,11 @@ static int gen6_ppgtt_init_scratch(struct gen6_ppgtt *ppgtt)
 static void gen6_ppgtt_free_pd(struct gen6_ppgtt *ppgtt)
 {
 	struct i915_page_directory * const pd = ppgtt->base.pd;
-	struct i915_page_dma * const scratch =
-		px_base(&ppgtt->base.vm.scratch[1]);
 	struct i915_page_table *pt;
 	u32 pde;
 
 	gen6_for_all_pdes(pt, pd, pde)
-		if (px_base(pt) != scratch)
+		if (pt)
 			free_px(&ppgtt->base.vm, pt);
 }
 
@@ -297,7 +299,7 @@ static void pd_vma_bind(struct i915_address_space *vm,
 	struct gen6_ppgtt *ppgtt = vma->private;
 	u32 ggtt_offset = i915_ggtt_offset(vma) / I915_GTT_PAGE_SIZE;
 
-	px_base(ppgtt->base.pd)->ggtt_offset = ggtt_offset * sizeof(gen6_pte_t);
+	ppgtt->pp_dir = ggtt_offset * sizeof(gen6_pte_t) << 10;
 	ppgtt->pd_addr = (gen6_pte_t __iomem *)ggtt->gsm + ggtt_offset;
 
 	gen6_flush_pd(ppgtt, 0, ppgtt->base.vm.total);
@@ -307,8 +309,6 @@ static void pd_vma_unbind(struct i915_address_space *vm, struct i915_vma *vma)
 {
 	struct gen6_ppgtt *ppgtt = vma->private;
 	struct i915_page_directory * const pd = ppgtt->base.pd;
-	struct i915_page_dma * const scratch =
-		px_base(&ppgtt->base.vm.scratch[1]);
 	struct i915_page_table *pt;
 	unsigned int pde;
 
@@ -317,11 +317,11 @@ static void pd_vma_unbind(struct i915_address_space *vm, struct i915_vma *vma)
 
 	/* Free all no longer used page tables */
 	gen6_for_all_pdes(pt, ppgtt->base.pd, pde) {
-		if (px_base(pt) == scratch || atomic_read(&pt->used))
+		if (!pt || atomic_read(&pt->used))
 			continue;
 
 		free_px(&ppgtt->base.vm, pt);
-		pd->entry[pde] = scratch;
+		pd->entry[pde] = NULL;
 	}
 
 	ppgtt->scan_for_unused_pt = false;
@@ -441,6 +441,7 @@ struct i915_ppgtt *gen6_ppgtt_create(struct intel_gt *gt)
 	ppgtt->base.vm.insert_entries = gen6_ppgtt_insert_entries;
 	ppgtt->base.vm.cleanup = gen6_ppgtt_cleanup;
 
+	ppgtt->base.vm.alloc_pt_dma = alloc_pt_dma;
 	ppgtt->base.vm.pte_encode = ggtt->vm.pte_encode;
 
 	ppgtt->base.pd = __alloc_pd(sizeof(*ppgtt->base.pd));
diff --git a/drivers/gpu/drm/i915/gt/gen6_ppgtt.h b/drivers/gpu/drm/i915/gt/gen6_ppgtt.h
index 72e481806c96..7249672e5802 100644
--- a/drivers/gpu/drm/i915/gt/gen6_ppgtt.h
+++ b/drivers/gpu/drm/i915/gt/gen6_ppgtt.h
@@ -14,6 +14,7 @@ struct gen6_ppgtt {
 	struct mutex flush;
 	struct i915_vma *vma;
 	gen6_pte_t __iomem *pd_addr;
+	u32 pp_dir;
 
 	atomic_t pin_count;
 	struct mutex pin_mutex;
diff --git a/drivers/gpu/drm/i915/gt/gen8_ppgtt.c b/drivers/gpu/drm/i915/gt/gen8_ppgtt.c
index e6f2acd445dd..f987781382d5 100644
--- a/drivers/gpu/drm/i915/gt/gen8_ppgtt.c
+++ b/drivers/gpu/drm/i915/gt/gen8_ppgtt.c
@@ -199,7 +199,7 @@ static u64 __gen8_ppgtt_clear(struct i915_address_space * const vm,
 			      struct i915_page_directory * const pd,
 			      u64 start, const u64 end, int lvl)
 {
-	const struct i915_page_scratch * const scratch = &vm->scratch[lvl];
+	const struct drm_i915_gem_object * const scratch = vm->scratch[lvl];
 	unsigned int idx, len;
 
 	GEM_BUG_ON(end > vm->total >> GEN8_PTE_SHIFT);
@@ -239,7 +239,7 @@ static u64 __gen8_ppgtt_clear(struct i915_address_space * const vm,
 
 			vaddr = kmap_atomic_px(pt);
 			memset64(vaddr + gen8_pd_index(start, 0),
-				 vm->scratch[0].encode,
+				 vm->scratch[0]->encode,
 				 count);
 			kunmap_atomic(vaddr);
 
@@ -296,12 +296,13 @@ static void __gen8_ppgtt_alloc(struct i915_address_space * const vm,
 			    __func__, vm, lvl + 1, idx);
 
 			pt = stash->pt[!!lvl];
-			GEM_BUG_ON(!pt);
+			__i915_gem_object_pin_pages(pt->base);
+			i915_gem_object_make_unshrinkable(pt->base);
 
 			if (lvl ||
 			    gen8_pt_count(*start, end) < I915_PDES ||
 			    intel_vgpu_active(vm->i915))
-				fill_px(pt, vm->scratch[lvl].encode);
+				fill_px(pt, vm->scratch[lvl]->encode);
 
 			spin_lock(&pd->lock);
 			if (likely(!pd->entry[idx])) {
@@ -356,16 +357,6 @@ static void gen8_ppgtt_alloc(struct i915_address_space *vm,
 			   &start, start + length, vm->top);
 }
 
-static __always_inline void
-write_pte(gen8_pte_t *pte, const gen8_pte_t val)
-{
-	/* Magic delays? Or can we refine these to flush all in one pass? */
-	*pte = val;
-	wmb(); /* cpu to cache */
-	clflush(pte); /* cache to memory */
-	wmb(); /* visible to all */
-}
-
 static __always_inline u64
 gen8_ppgtt_insert_pte(struct i915_ppgtt *ppgtt,
 		      struct i915_page_directory *pdp,
@@ -382,8 +373,7 @@ gen8_ppgtt_insert_pte(struct i915_ppgtt *ppgtt,
 	vaddr = kmap_atomic_px(i915_pt_entry(pd, gen8_pd_index(idx, 1)));
 	do {
 		GEM_BUG_ON(iter->sg->length < I915_GTT_PAGE_SIZE);
-		write_pte(&vaddr[gen8_pd_index(idx, 0)],
-			  pte_encode | iter->dma);
+		vaddr[gen8_pd_index(idx, 0)] = pte_encode | iter->dma;
 
 		iter->dma += I915_GTT_PAGE_SIZE;
 		if (iter->dma >= iter->max) {
@@ -406,10 +396,12 @@ gen8_ppgtt_insert_pte(struct i915_ppgtt *ppgtt,
 				pd = pdp->entry[gen8_pd_index(idx, 2)];
 			}
 
+			clflush_cache_range(vaddr, PAGE_SIZE);
 			kunmap_atomic(vaddr);
 			vaddr = kmap_atomic_px(i915_pt_entry(pd, gen8_pd_index(idx, 1)));
 		}
 	} while (1);
+	clflush_cache_range(vaddr, PAGE_SIZE);
 	kunmap_atomic(vaddr);
 
 	return idx;
@@ -465,7 +457,7 @@ static void gen8_ppgtt_insert_huge(struct i915_vma *vma,
 
 		do {
 			GEM_BUG_ON(iter->sg->length < page_size);
-			write_pte(&vaddr[index++], encode | iter->dma);
+			vaddr[index++] = encode | iter->dma;
 
 			start += page_size;
 			iter->dma += page_size;
@@ -490,6 +482,7 @@ static void gen8_ppgtt_insert_huge(struct i915_vma *vma,
 			}
 		} while (rem >= page_size && index < I915_PDES);
 
+		clflush_cache_range(vaddr, PAGE_SIZE);
 		kunmap_atomic(vaddr);
 
 		/*
@@ -521,7 +514,7 @@ static void gen8_ppgtt_insert_huge(struct i915_vma *vma,
 			if (I915_SELFTEST_ONLY(vma->vm->scrub_64K)) {
 				u16 i;
 
-				encode = vma->vm->scratch[0].encode;
+				encode = vma->vm->scratch[0]->encode;
 				vaddr = kmap_atomic_px(i915_pt_entry(pd, maybe_64K));
 
 				for (i = 1; i < index; i += 16)
@@ -575,27 +568,37 @@ static int gen8_init_scratch(struct i915_address_space *vm)
 		GEM_BUG_ON(!clone->has_read_only);
 
 		vm->scratch_order = clone->scratch_order;
-		memcpy(vm->scratch, clone->scratch, sizeof(vm->scratch));
-		px_dma(&vm->scratch[0]) = 0; /* no xfer of ownership */
+		for (i = 0; i <= vm->top; i++)
+			vm->scratch[i] = i915_gem_object_get(clone->scratch[i]);
+
 		return 0;
 	}
 
-	ret = setup_scratch_page(vm, __GFP_HIGHMEM);
+	ret = setup_scratch_page(vm);
 	if (ret)
 		return ret;
 
-	vm->scratch[0].encode =
-		gen8_pte_encode(px_dma(&vm->scratch[0]),
+	vm->scratch[0]->encode =
+		gen8_pte_encode(px_dma(vm->scratch[0]),
 				I915_CACHE_LLC, vm->has_read_only);
 
 	for (i = 1; i <= vm->top; i++) {
-		if (unlikely(setup_page_dma(vm, px_base(&vm->scratch[i]))))
+		struct drm_i915_gem_object *obj;
+
+		obj = vm->alloc_pt_dma(vm, I915_GTT_PAGE_SIZE_4K);
+		if (IS_ERR(obj))
 			goto free_scratch;
 
-		fill_px(&vm->scratch[i], vm->scratch[i - 1].encode);
-		vm->scratch[i].encode =
-			gen8_pde_encode(px_dma(&vm->scratch[i]),
-					I915_CACHE_LLC);
+		ret = pin_pt_dma(vm, obj);
+		if (ret) {
+			i915_gem_object_put(obj);
+			goto free_scratch;
+		}
+
+		fill_px(obj, vm->scratch[i - 1]->encode);
+		obj->encode = gen8_pde_encode(px_dma(obj), I915_CACHE_LLC);
+
+		vm->scratch[i] = obj;
 	}
 
 	return 0;
@@ -616,12 +619,20 @@ static int gen8_preallocate_top_level_pdp(struct i915_ppgtt *ppgtt)
 
 	for (idx = 0; idx < GEN8_3LVL_PDPES; idx++) {
 		struct i915_page_directory *pde;
+		int err;
 
 		pde = alloc_pd(vm);
 		if (IS_ERR(pde))
 			return PTR_ERR(pde);
 
-		fill_px(pde, vm->scratch[1].encode);
+		err = pin_pt_dma(vm, pde->pt.base);
+		if (err) {
+			i915_gem_object_put(pde->pt.base);
+			kfree(pde);
+			return err;
+		}
+
+		fill_px(pde, vm->scratch[1]->encode);
 		set_pd_entry(pd, idx, pde);
 		atomic_inc(px_used(pde)); /* keep pinned */
 	}
@@ -635,6 +646,7 @@ gen8_alloc_top_pd(struct i915_address_space *vm)
 {
 	const unsigned int count = gen8_pd_top_count(vm);
 	struct i915_page_directory *pd;
+	int err;
 
 	GEM_BUG_ON(count > ARRAY_SIZE(pd->entry));
 
@@ -642,12 +654,20 @@ gen8_alloc_top_pd(struct i915_address_space *vm)
 	if (unlikely(!pd))
 		return ERR_PTR(-ENOMEM);
 
-	if (unlikely(setup_page_dma(vm, px_base(pd)))) {
+	pd->pt.base = vm->alloc_pt_dma(vm, I915_GTT_PAGE_SIZE_4K);
+	if (IS_ERR(pd->pt.base)) {
 		kfree(pd);
 		return ERR_PTR(-ENOMEM);
 	}
 
-	fill_page_dma(px_base(pd), vm->scratch[vm->top].encode, count);
+	err = pin_pt_dma(vm, pd->pt.base);
+	if (err) {
+		i915_gem_object_put(pd->pt.base);
+		kfree(pd);
+		return ERR_PTR(err);
+	}
+
+	fill_page_dma(px_base(pd), vm->scratch[vm->top]->encode, count);
 	atomic_inc(px_used(pd)); /* mark as pinned */
 	return pd;
 }
@@ -681,12 +701,7 @@ struct i915_ppgtt *gen8_ppgtt_create(struct intel_gt *gt)
 	 */
 	ppgtt->vm.has_read_only = !IS_GEN_RANGE(gt->i915, 11, 12);
 
-	/*
-	 * There are only few exceptions for gen >=6. chv and bxt.
-	 * And we are not sure about the latter so play safe for now.
-	 */
-	if (IS_CHERRYVIEW(gt->i915) || IS_BROXTON(gt->i915))
-		ppgtt->vm.pt_kmap_wc = true;
+	ppgtt->vm.alloc_pt_dma = alloc_pt_dma;
 
 	err = gen8_init_scratch(&ppgtt->vm);
 	if (err)
diff --git a/drivers/gpu/drm/i915/gt/intel_ggtt.c b/drivers/gpu/drm/i915/gt/intel_ggtt.c
index 5a33056ab976..33a3f627ddb1 100644
--- a/drivers/gpu/drm/i915/gt/intel_ggtt.c
+++ b/drivers/gpu/drm/i915/gt/intel_ggtt.c
@@ -78,8 +78,6 @@ int i915_ggtt_init_hw(struct drm_i915_private *i915)
 {
 	int ret;
 
-	stash_init(&i915->mm.wc_stash);
-
 	/*
 	 * Note that we use page colouring to enforce a guard page at the
 	 * end of the address space. This is required as the CS may prefetch
@@ -232,7 +230,7 @@ static void gen8_ggtt_insert_entries(struct i915_address_space *vm,
 
 	/* Fill the allocated but "unused" space beyond the end of the buffer */
 	while (gte < end)
-		gen8_set_pte(gte++, vm->scratch[0].encode);
+		gen8_set_pte(gte++, vm->scratch[0]->encode);
 
 	/*
 	 * We want to flush the TLBs only after we're certain all the PTE
@@ -283,7 +281,7 @@ static void gen6_ggtt_insert_entries(struct i915_address_space *vm,
 
 	/* Fill the allocated but "unused" space beyond the end of the buffer */
 	while (gte < end)
-		iowrite32(vm->scratch[0].encode, gte++);
+		iowrite32(vm->scratch[0]->encode, gte++);
 
 	/*
 	 * We want to flush the TLBs only after we're certain all the PTE
@@ -303,7 +301,7 @@ static void gen8_ggtt_clear_range(struct i915_address_space *vm,
 	struct i915_ggtt *ggtt = i915_vm_to_ggtt(vm);
 	unsigned int first_entry = start / I915_GTT_PAGE_SIZE;
 	unsigned int num_entries = length / I915_GTT_PAGE_SIZE;
-	const gen8_pte_t scratch_pte = vm->scratch[0].encode;
+	const gen8_pte_t scratch_pte = vm->scratch[0]->encode;
 	gen8_pte_t __iomem *gtt_base =
 		(gen8_pte_t __iomem *)ggtt->gsm + first_entry;
 	const int max_entries = ggtt_total_entries(ggtt) - first_entry;
@@ -401,7 +399,7 @@ static void gen6_ggtt_clear_range(struct i915_address_space *vm,
 		 first_entry, num_entries, max_entries))
 		num_entries = max_entries;
 
-	scratch_pte = vm->scratch[0].encode;
+	scratch_pte = vm->scratch[0]->encode;
 	for (i = 0; i < num_entries; i++)
 		iowrite32(scratch_pte, &gtt_base[i]);
 }
@@ -617,6 +615,10 @@ static int init_aliasing_ppgtt(struct i915_ggtt *ggtt)
 	if (err)
 		goto err_ppgtt;
 
+	err = i915_vm_pin_pt_stash(&ppgtt->vm, &stash);
+	if (err)
+		goto err_stash;
+
 	/*
 	 * Note we only pre-allocate as far as the end of the global
 	 * GTT. On 48b / 4-level page-tables, the difference is very,
@@ -637,6 +639,8 @@ static int init_aliasing_ppgtt(struct i915_ggtt *ggtt)
 	i915_vm_free_pt_stash(&ppgtt->vm, &stash);
 	return 0;
 
+err_stash:
+	i915_vm_free_pt_stash(&ppgtt->vm, &stash);
 err_ppgtt:
 	i915_vm_put(&ppgtt->vm);
 	return err;
@@ -712,18 +716,11 @@ static void ggtt_cleanup_hw(struct i915_ggtt *ggtt)
 void i915_ggtt_driver_release(struct drm_i915_private *i915)
 {
 	struct i915_ggtt *ggtt = &i915->ggtt;
-	struct pagevec *pvec;
 
 	fini_aliasing_ppgtt(ggtt);
 
 	intel_ggtt_fini_fences(ggtt);
 	ggtt_cleanup_hw(ggtt);
-
-	pvec = &i915->mm.wc_stash.pvec;
-	if (pvec->nr) {
-		set_pages_array_wb(pvec->pages, pvec->nr);
-		__pagevec_release(pvec);
-	}
 }
 
 static unsigned int gen6_get_total_gtt_size(u16 snb_gmch_ctl)
@@ -786,7 +783,7 @@ static int ggtt_probe_common(struct i915_ggtt *ggtt, u64 size)
 		return -ENOMEM;
 	}
 
-	ret = setup_scratch_page(&ggtt->vm, GFP_DMA32);
+	ret = setup_scratch_page(&ggtt->vm);
 	if (ret) {
 		drm_err(&i915->drm, "Scratch setup failed\n");
 		/* iounmap will also get called at remove, but meh */
@@ -794,8 +791,8 @@ static int ggtt_probe_common(struct i915_ggtt *ggtt, u64 size)
 		return ret;
 	}
 
-	ggtt->vm.scratch[0].encode =
-		ggtt->vm.pte_encode(px_dma(&ggtt->vm.scratch[0]),
+	ggtt->vm.scratch[0]->encode =
+		ggtt->vm.pte_encode(px_dma(ggtt->vm.scratch[0]),
 				    I915_CACHE_NONE, 0);
 
 	return 0;
@@ -821,7 +818,7 @@ static void gen6_gmch_remove(struct i915_address_space *vm)
 	struct i915_ggtt *ggtt = i915_vm_to_ggtt(vm);
 
 	iounmap(ggtt->gsm);
-	cleanup_scratch_page(vm);
+	free_scratch(vm);
 }
 
 static struct resource pci_resource(struct pci_dev *pdev, int bar)
@@ -849,6 +846,8 @@ static int gen8_gmch_probe(struct i915_ggtt *ggtt)
 	else
 		size = gen8_get_total_gtt_size(snb_gmch_ctl);
 
+	ggtt->vm.alloc_pt_dma = alloc_pt_dma;
+
 	ggtt->vm.total = (size / sizeof(gen8_pte_t)) * I915_GTT_PAGE_SIZE;
 	ggtt->vm.cleanup = gen6_gmch_remove;
 	ggtt->vm.insert_page = gen8_ggtt_insert_page;
@@ -997,6 +996,8 @@ static int gen6_gmch_probe(struct i915_ggtt *ggtt)
 	size = gen6_get_total_gtt_size(snb_gmch_ctl);
 	ggtt->vm.total = (size / sizeof(gen6_pte_t)) * I915_GTT_PAGE_SIZE;
 
+	ggtt->vm.alloc_pt_dma = alloc_pt_dma;
+
 	ggtt->vm.clear_range = nop_clear_range;
 	if (!HAS_FULL_PPGTT(i915) || intel_scanout_needs_vtd_wa(i915))
 		ggtt->vm.clear_range = gen6_ggtt_clear_range;
@@ -1047,6 +1048,8 @@ static int i915_gmch_probe(struct i915_ggtt *ggtt)
 	ggtt->gmadr =
 		(struct resource)DEFINE_RES_MEM(gmadr_base, ggtt->mappable_end);
 
+	ggtt->vm.alloc_pt_dma = alloc_pt_dma;
+
 	ggtt->do_idle_maps = needs_idle_maps(i915);
 	ggtt->vm.insert_page = i915_ggtt_insert_page;
 	ggtt->vm.insert_entries = i915_ggtt_insert_entries;
diff --git a/drivers/gpu/drm/i915/gt/intel_gtt.c b/drivers/gpu/drm/i915/gt/intel_gtt.c
index 2a72cce63fd9..5df35e5a8936 100644
--- a/drivers/gpu/drm/i915/gt/intel_gtt.c
+++ b/drivers/gpu/drm/i915/gt/intel_gtt.c
@@ -11,160 +11,21 @@
 #include "intel_gt.h"
 #include "intel_gtt.h"
 
-void stash_init(struct pagestash *stash)
+struct drm_i915_gem_object *alloc_pt_dma(struct i915_address_space *vm, int sz)
 {
-	pagevec_init(&stash->pvec);
-	spin_lock_init(&stash->lock);
+	return i915_gem_object_create_internal(vm->i915, sz);
 }
 
-static struct page *stash_pop_page(struct pagestash *stash)
+int pin_pt_dma(struct i915_address_space *vm, struct drm_i915_gem_object *obj)
 {
-	struct page *page = NULL;
+	int err;
 
-	spin_lock(&stash->lock);
-	if (likely(stash->pvec.nr))
-		page = stash->pvec.pages[--stash->pvec.nr];
-	spin_unlock(&stash->lock);
+	err = i915_gem_object_pin_pages(obj);
+	if (err)
+		return err;
 
-	return page;
-}
-
-static void stash_push_pagevec(struct pagestash *stash, struct pagevec *pvec)
-{
-	unsigned int nr;
-
-	spin_lock_nested(&stash->lock, SINGLE_DEPTH_NESTING);
-
-	nr = min_t(typeof(nr), pvec->nr, pagevec_space(&stash->pvec));
-	memcpy(stash->pvec.pages + stash->pvec.nr,
-	       pvec->pages + pvec->nr - nr,
-	       sizeof(pvec->pages[0]) * nr);
-	stash->pvec.nr += nr;
-
-	spin_unlock(&stash->lock);
-
-	pvec->nr -= nr;
-}
-
-static struct page *vm_alloc_page(struct i915_address_space *vm, gfp_t gfp)
-{
-	struct pagevec stack;
-	struct page *page;
-
-	if (I915_SELFTEST_ONLY(should_fail(&vm->fault_attr, 1)))
-		i915_gem_shrink_all(vm->i915);
-
-	page = stash_pop_page(&vm->free_pages);
-	if (page)
-		return page;
-
-	if (!vm->pt_kmap_wc)
-		return alloc_page(gfp);
-
-	/* Look in our global stash of WC pages... */
-	page = stash_pop_page(&vm->i915->mm.wc_stash);
-	if (page)
-		return page;
-
-	/*
-	 * Otherwise batch allocate pages to amortize cost of set_pages_wc.
-	 *
-	 * We have to be careful as page allocation may trigger the shrinker
-	 * (via direct reclaim) which will fill up the WC stash underneath us.
-	 * So we add our WB pages into a temporary pvec on the stack and merge
-	 * them into the WC stash after all the allocations are complete.
-	 */
-	pagevec_init(&stack);
-	do {
-		struct page *page;
-
-		page = alloc_page(gfp);
-		if (unlikely(!page))
-			break;
-
-		stack.pages[stack.nr++] = page;
-	} while (pagevec_space(&stack));
-
-	if (stack.nr && !set_pages_array_wc(stack.pages, stack.nr)) {
-		page = stack.pages[--stack.nr];
-
-		/* Merge spare WC pages to the global stash */
-		if (stack.nr)
-			stash_push_pagevec(&vm->i915->mm.wc_stash, &stack);
-
-		/* Push any surplus WC pages onto the local VM stash */
-		if (stack.nr)
-			stash_push_pagevec(&vm->free_pages, &stack);
-	}
-
-	/* Return unwanted leftovers */
-	if (unlikely(stack.nr)) {
-		WARN_ON_ONCE(set_pages_array_wb(stack.pages, stack.nr));
-		__pagevec_release(&stack);
-	}
-
-	return page;
-}
-
-static void vm_free_pages_release(struct i915_address_space *vm,
-				  bool immediate)
-{
-	struct pagevec *pvec = &vm->free_pages.pvec;
-	struct pagevec stack;
-
-	lockdep_assert_held(&vm->free_pages.lock);
-	GEM_BUG_ON(!pagevec_count(pvec));
-
-	if (vm->pt_kmap_wc) {
-		/*
-		 * When we use WC, first fill up the global stash and then
-		 * only if full immediately free the overflow.
-		 */
-		stash_push_pagevec(&vm->i915->mm.wc_stash, pvec);
-
-		/*
-		 * As we have made some room in the VM's free_pages,
-		 * we can wait for it to fill again. Unless we are
-		 * inside i915_address_space_fini() and must
-		 * immediately release the pages!
-		 */
-		if (pvec->nr <= (immediate ? 0 : PAGEVEC_SIZE - 1))
-			return;
-
-		/*
-		 * We have to drop the lock to allow ourselves to sleep,
-		 * so take a copy of the pvec and clear the stash for
-		 * others to use it as we sleep.
-		 */
-		stack = *pvec;
-		pagevec_reinit(pvec);
-		spin_unlock(&vm->free_pages.lock);
-
-		pvec = &stack;
-		set_pages_array_wb(pvec->pages, pvec->nr);
-
-		spin_lock(&vm->free_pages.lock);
-	}
-
-	__pagevec_release(pvec);
-}
-
-static void vm_free_page(struct i915_address_space *vm, struct page *page)
-{
-	/*
-	 * On !llc, we need to change the pages back to WB. We only do so
-	 * in bulk, so we rarely need to change the page attributes here,
-	 * but doing so requires a stop_machine() from deep inside arch/x86/mm.
-	 * To make detection of the possible sleep more likely, use an
-	 * unconditional might_sleep() for everybody.
-	 */
-	might_sleep();
-	spin_lock(&vm->free_pages.lock);
-	while (!pagevec_space(&vm->free_pages.pvec))
-		vm_free_pages_release(vm, false);
-	GEM_BUG_ON(pagevec_count(&vm->free_pages.pvec) >= PAGEVEC_SIZE);
-	pagevec_add(&vm->free_pages.pvec, page);
-	spin_unlock(&vm->free_pages.lock);
+	i915_gem_object_make_unshrinkable(obj);
+	return 0;
 }
 
 void __i915_vm_close(struct i915_address_space *vm)
@@ -194,14 +55,7 @@ void __i915_vm_close(struct i915_address_space *vm)
 
 void i915_address_space_fini(struct i915_address_space *vm)
 {
-	spin_lock(&vm->free_pages.lock);
-	if (pagevec_count(&vm->free_pages.pvec))
-		vm_free_pages_release(vm, true);
-	GEM_BUG_ON(pagevec_count(&vm->free_pages.pvec));
-	spin_unlock(&vm->free_pages.lock);
-
 	drm_mm_takedown(&vm->mm);
-
 	mutex_destroy(&vm->mutex);
 }
 
@@ -246,8 +100,6 @@ void i915_address_space_init(struct i915_address_space *vm, int subclass)
 	drm_mm_init(&vm->mm, 0, vm->total);
 	vm->mm.head_node.color = I915_COLOR_UNEVICTABLE;
 
-	stash_init(&vm->free_pages);
-
 	INIT_LIST_HEAD(&vm->bound_list);
 }
 
@@ -264,64 +116,48 @@ void clear_pages(struct i915_vma *vma)
 	memset(&vma->page_sizes, 0, sizeof(vma->page_sizes));
 }
 
-static int __setup_page_dma(struct i915_address_space *vm,
-			    struct i915_page_dma *p,
-			    gfp_t gfp)
-{
-	p->page = vm_alloc_page(vm, gfp | I915_GFP_ALLOW_FAIL);
-	if (unlikely(!p->page))
-		return -ENOMEM;
-
-	p->daddr = dma_map_page_attrs(vm->dma,
-				      p->page, 0, PAGE_SIZE,
-				      PCI_DMA_BIDIRECTIONAL,
-				      DMA_ATTR_SKIP_CPU_SYNC |
-				      DMA_ATTR_NO_WARN);
-	if (unlikely(dma_mapping_error(vm->dma, p->daddr))) {
-		vm_free_page(vm, p->page);
-		return -ENOMEM;
-	}
-
-	return 0;
-}
-
-int setup_page_dma(struct i915_address_space *vm, struct i915_page_dma *p)
+dma_addr_t __px_dma(struct drm_i915_gem_object *p)
 {
-	return __setup_page_dma(vm, p, __GFP_HIGHMEM);
+	GEM_BUG_ON(!i915_gem_object_has_pages(p));
+	return sg_dma_address(p->mm.pages->sgl);
 }
 
-void cleanup_page_dma(struct i915_address_space *vm, struct i915_page_dma *p)
+struct page *__px_page(struct drm_i915_gem_object *p)
 {
-	dma_unmap_page(vm->dma, p->daddr, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
-	vm_free_page(vm, p->page);
+	GEM_BUG_ON(!i915_gem_object_has_pages(p));
+	return sg_page(p->mm.pages->sgl);
 }
 
 void
-fill_page_dma(const struct i915_page_dma *p, const u64 val, unsigned int count)
+fill_page_dma(struct drm_i915_gem_object *p, const u64 val, unsigned int count)
 {
-	kunmap_atomic(memset64(kmap_atomic(p->page), val, count));
+	struct page *page = __px_page(p);
+	void *vaddr;
+
+	vaddr = kmap(page);
+	memset64(vaddr, val, count);
+	clflush_cache_range(vaddr, PAGE_SIZE);
+	kunmap(page);
 }
 
-static void poison_scratch_page(struct page *page, unsigned long size)
+static void poison_scratch_page(struct drm_i915_gem_object *scratch)
 {
+	struct sgt_iter sgt;
+	struct page *page;
+
 	if (!IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM))
 		return;
 
-	GEM_BUG_ON(!IS_ALIGNED(size, PAGE_SIZE));
-
-	do {
+	for_each_sgt_page(page, sgt, scratch->mm.pages) {
 		void *vaddr;
 
 		vaddr = kmap(page);
 		memset(vaddr, POISON_FREE, PAGE_SIZE);
 		kunmap(page);
-
-		page = pfn_to_page(page_to_pfn(page) + 1);
-		size -= PAGE_SIZE;
-	} while (size);
+	}
 }
 
-int setup_scratch_page(struct i915_address_space *vm, gfp_t gfp)
+int setup_scratch_page(struct i915_address_space *vm)
 {
 	unsigned long size;
 
@@ -338,21 +174,22 @@ int setup_scratch_page(struct i915_address_space *vm, gfp_t gfp)
 	 */
 	size = I915_GTT_PAGE_SIZE_4K;
 	if (i915_vm_is_4lvl(vm) &&
-	    HAS_PAGE_SIZES(vm->i915, I915_GTT_PAGE_SIZE_64K)) {
+	    HAS_PAGE_SIZES(vm->i915, I915_GTT_PAGE_SIZE_64K))
 		size = I915_GTT_PAGE_SIZE_64K;
-		gfp |= __GFP_NOWARN;
-	}
-	gfp |= __GFP_ZERO | __GFP_RETRY_MAYFAIL;
 
 	do {
-		unsigned int order = get_order(size);
-		struct page *page;
-		dma_addr_t addr;
+		struct drm_i915_gem_object *obj;
 
-		page = alloc_pages(gfp, order);
-		if (unlikely(!page))
+		obj = vm->alloc_pt_dma(vm, size);
+		if (IS_ERR(obj))
 			goto skip;
 
+		if (pin_pt_dma(vm, obj))
+			goto skip_obj;
+
+		if (obj->mm.page_sizes.sg < size)
+			goto skip_obj;
+
 		/*
 		 * Use a non-zero scratch page for debugging.
 		 *
@@ -362,61 +199,28 @@ int setup_scratch_page(struct i915_address_space *vm, gfp_t gfp)
 		 * should it ever be accidentally used, the effect should be
 		 * fairly benign.
 		 */
-		poison_scratch_page(page, size);
-
-		addr = dma_map_page_attrs(vm->dma,
-					  page, 0, size,
-					  PCI_DMA_BIDIRECTIONAL,
-					  DMA_ATTR_SKIP_CPU_SYNC |
-					  DMA_ATTR_NO_WARN);
-		if (unlikely(dma_mapping_error(vm->dma, addr)))
-			goto free_page;
-
-		if (unlikely(!IS_ALIGNED(addr, size)))
-			goto unmap_page;
-
-		vm->scratch[0].base.page = page;
-		vm->scratch[0].base.daddr = addr;
-		vm->scratch_order = order;
+		poison_scratch_page(obj);
+
+		vm->scratch[0] = obj;
+		vm->scratch_order = get_order(size);
 		return 0;
 
-unmap_page:
-		dma_unmap_page(vm->dma, addr, size, PCI_DMA_BIDIRECTIONAL);
-free_page:
-		__free_pages(page, order);
+skip_obj:
+		i915_gem_object_put(obj);
 skip:
 		if (size == I915_GTT_PAGE_SIZE_4K)
 			return -ENOMEM;
 
 		size = I915_GTT_PAGE_SIZE_4K;
-		gfp &= ~__GFP_NOWARN;
 	} while (1);
 }
 
-void cleanup_scratch_page(struct i915_address_space *vm)
-{
-	struct i915_page_dma *p = px_base(&vm->scratch[0]);
-	unsigned int order = vm->scratch_order;
-
-	dma_unmap_page(vm->dma, p->daddr, BIT(order) << PAGE_SHIFT,
-		       PCI_DMA_BIDIRECTIONAL);
-	__free_pages(p->page, order);
-}
-
 void free_scratch(struct i915_address_space *vm)
 {
 	int i;
 
-	if (!px_dma(&vm->scratch[0])) /* set to 0 on clones */
-		return;
-
-	for (i = 1; i <= vm->top; i++) {
-		if (!px_dma(&vm->scratch[i]))
-			break;
-		cleanup_page_dma(vm, px_base(&vm->scratch[i]));
-	}
-
-	cleanup_scratch_page(vm);
+	for (i = 0; i <= vm->top; i++)
+		i915_gem_object_put(vm->scratch[i]);
 }
 
 void gtt_write_workarounds(struct intel_gt *gt)
diff --git a/drivers/gpu/drm/i915/gt/intel_gtt.h b/drivers/gpu/drm/i915/gt/intel_gtt.h
index 8bd462d2fcd9..6e1bedf5448b 100644
--- a/drivers/gpu/drm/i915/gt/intel_gtt.h
+++ b/drivers/gpu/drm/i915/gt/intel_gtt.h
@@ -134,31 +134,19 @@ typedef u64 gen8_pte_t;
 #define GEN8_PDE_IPS_64K BIT(11)
 #define GEN8_PDE_PS_2M   BIT(7)
 
+enum i915_cache_level;
+
+struct drm_i915_file_private;
+struct drm_i915_gem_object;
 struct i915_fence_reg;
+struct i915_vma;
+struct intel_gt;
 
 #define for_each_sgt_daddr(__dp, __iter, __sgt) \
 	__for_each_sgt_daddr(__dp, __iter, __sgt, I915_GTT_PAGE_SIZE)
 
-struct i915_page_dma {
-	struct page *page;
-	union {
-		dma_addr_t daddr;
-
-		/*
-		 * For gen6/gen7 only. This is the offset in the GGTT
-		 * where the page directory entries for PPGTT begin
-		 */
-		u32 ggtt_offset;
-	};
-};
-
-struct i915_page_scratch {
-	struct i915_page_dma base;
-	u64 encode;
-};
-
 struct i915_page_table {
-	struct i915_page_dma base;
+	struct drm_i915_gem_object *base;
 	union {
 		atomic_t used;
 		struct i915_page_table *stash;
@@ -179,12 +167,14 @@ struct i915_page_directory {
 	other)
 
 #define px_base(px) \
-	__px_choose_expr(px, struct i915_page_dma *, __x, \
-	__px_choose_expr(px, struct i915_page_scratch *, &__x->base, \
-	__px_choose_expr(px, struct i915_page_table *, &__x->base, \
-	__px_choose_expr(px, struct i915_page_directory *, &__x->pt.base, \
-	(void)0))))
-#define px_dma(px) (px_base(px)->daddr)
+	__px_choose_expr(px, struct drm_i915_gem_object *, __x, \
+	__px_choose_expr(px, struct i915_page_table *, __x->base, \
+	__px_choose_expr(px, struct i915_page_directory *, __x->pt.base, \
+	(void)0)))
+
+struct page *__px_page(struct drm_i915_gem_object *p);
+dma_addr_t __px_dma(struct drm_i915_gem_object *p);
+#define px_dma(px) (__px_dma(px_base(px)))
 
 #define px_pt(px) \
 	__px_choose_expr(px, struct i915_page_table *, __x, \
@@ -192,13 +182,6 @@ struct i915_page_directory {
 	(void)0))
 #define px_used(px) (&px_pt(px)->used)
 
-enum i915_cache_level;
-
-struct drm_i915_file_private;
-struct drm_i915_gem_object;
-struct i915_vma;
-struct intel_gt;
-
 struct i915_vm_pt_stash {
 	/* preallocated chains of page tables/directories */
 	struct i915_page_table *pt[2];
@@ -222,13 +205,6 @@ struct i915_vma_ops {
 	void (*clear_pages)(struct i915_vma *vma);
 };
 
-struct pagestash {
-	spinlock_t lock;
-	struct pagevec pvec;
-};
-
-void stash_init(struct pagestash *stash);
-
 struct i915_address_space {
 	struct kref ref;
 	struct rcu_work rcu;
@@ -265,7 +241,7 @@ struct i915_address_space {
 #define VM_CLASS_GGTT 0
 #define VM_CLASS_PPGTT 1
 
-	struct i915_page_scratch scratch[4];
+	struct drm_i915_gem_object *scratch[4];
 	unsigned int scratch_order;
 	unsigned int top;
 
@@ -274,17 +250,15 @@ struct i915_address_space {
 	 */
 	struct list_head bound_list;
 
-	struct pagestash free_pages;
-
 	/* Global GTT */
 	bool is_ggtt:1;
 
-	/* Some systems require uncached updates of the page directories */
-	bool pt_kmap_wc:1;
-
 	/* Some systems support read-only mappings for GGTT and/or PPGTT */
 	bool has_read_only:1;
 
+	struct drm_i915_gem_object *
+		(*alloc_pt_dma)(struct i915_address_space *vm, int sz);
+
 	u64 (*pte_encode)(dma_addr_t addr,
 			  enum i915_cache_level level,
 			  u32 flags); /* Create a valid PTE */
@@ -500,9 +474,9 @@ i915_pd_entry(const struct i915_page_directory * const pdp,
 static inline dma_addr_t
 i915_page_dir_dma_addr(const struct i915_ppgtt *ppgtt, const unsigned int n)
 {
-	struct i915_page_dma *pt = ppgtt->pd->entry[n];
+	struct i915_page_table *pt = ppgtt->pd->entry[n];
 
-	return px_dma(pt ?: px_base(&ppgtt->vm.scratch[ppgtt->vm.top]));
+	return __px_dma(pt ? px_base(pt) : ppgtt->vm.scratch[ppgtt->vm.top]);
 }
 
 void ppgtt_init(struct i915_ppgtt *ppgtt, struct intel_gt *gt);
@@ -527,13 +501,10 @@ struct i915_ppgtt *i915_ppgtt_create(struct intel_gt *gt);
 void i915_ggtt_suspend(struct i915_ggtt *gtt);
 void i915_ggtt_resume(struct i915_ggtt *ggtt);
 
-int setup_page_dma(struct i915_address_space *vm, struct i915_page_dma *p);
-void cleanup_page_dma(struct i915_address_space *vm, struct i915_page_dma *p);
-
-#define kmap_atomic_px(px) kmap_atomic(px_base(px)->page)
+#define kmap_atomic_px(px) kmap_atomic(__px_page(px_base(px)))
 
 void
-fill_page_dma(const struct i915_page_dma *p, const u64 val, unsigned int count);
+fill_page_dma(struct drm_i915_gem_object *p, const u64 val, unsigned int count);
 
 #define fill_px(px, v) fill_page_dma(px_base(px), (v), PAGE_SIZE / sizeof(u64))
 #define fill32_px(px, v) do {						\
@@ -541,37 +512,38 @@ fill_page_dma(const struct i915_page_dma *p, const u64 val, unsigned int count);
 	fill_px((px), v__ << 32 | v__);					\
 } while (0)
 
-int setup_scratch_page(struct i915_address_space *vm, gfp_t gfp);
-void cleanup_scratch_page(struct i915_address_space *vm);
+int setup_scratch_page(struct i915_address_space *vm);
 void free_scratch(struct i915_address_space *vm);
 
+struct drm_i915_gem_object *alloc_pt_dma(struct i915_address_space *vm, int sz);
 struct i915_page_table *alloc_pt(struct i915_address_space *vm);
 struct i915_page_directory *alloc_pd(struct i915_address_space *vm);
 struct i915_page_directory *__alloc_pd(size_t sz);
 
-void free_pd(struct i915_address_space *vm, struct i915_page_dma *pd);
+int pin_pt_dma(struct i915_address_space *vm, struct drm_i915_gem_object *obj);
 
-#define free_px(vm, px) free_pd(vm, px_base(px))
+void free_pt(struct i915_address_space *vm, struct i915_page_table *pt);
+#define free_px(vm, px) free_pt(vm, px_pt(px))
 
 void
 __set_pd_entry(struct i915_page_directory * const pd,
 	       const unsigned short idx,
-	       struct i915_page_dma * const to,
+	       struct i915_page_table *pt,
 	       u64 (*encode)(const dma_addr_t, const enum i915_cache_level));
 
 #define set_pd_entry(pd, idx, to) \
-	__set_pd_entry((pd), (idx), px_base(to), gen8_pde_encode)
+	__set_pd_entry((pd), (idx), px_pt(to), gen8_pde_encode)
 
 void
 clear_pd_entry(struct i915_page_directory * const pd,
 	       const unsigned short idx,
-	       const struct i915_page_scratch * const scratch);
+	       const struct drm_i915_gem_object * const scratch);
 
 bool
 release_pd_entry(struct i915_page_directory * const pd,
 		 const unsigned short idx,
 		 struct i915_page_table * const pt,
-		 const struct i915_page_scratch * const scratch);
+		 const struct drm_i915_gem_object * const scratch);
 void gen6_ggtt_invalidate(struct i915_ggtt *ggtt);
 
 int ggtt_set_pages(struct i915_vma *vma);
@@ -593,6 +565,8 @@ void setup_private_pat(struct intel_uncore *uncore);
 int i915_vm_alloc_pt_stash(struct i915_address_space *vm,
 			   struct i915_vm_pt_stash *stash,
 			   u64 size);
+int i915_vm_pin_pt_stash(struct i915_address_space *vm,
+			 struct i915_vm_pt_stash *stash);
 void i915_vm_free_pt_stash(struct i915_address_space *vm,
 			   struct i915_vm_pt_stash *stash);
 
diff --git a/drivers/gpu/drm/i915/gt/intel_ppgtt.c b/drivers/gpu/drm/i915/gt/intel_ppgtt.c
index 9633fd2d294d..1215c1d6d9e3 100644
--- a/drivers/gpu/drm/i915/gt/intel_ppgtt.c
+++ b/drivers/gpu/drm/i915/gt/intel_ppgtt.c
@@ -18,7 +18,8 @@ struct i915_page_table *alloc_pt(struct i915_address_space *vm)
 	if (unlikely(!pt))
 		return ERR_PTR(-ENOMEM);
 
-	if (unlikely(setup_page_dma(vm, &pt->base))) {
+	pt->base = vm->alloc_pt_dma(vm, I915_GTT_PAGE_SIZE_4K);
+	if (IS_ERR(pt->base)) {
 		kfree(pt);
 		return ERR_PTR(-ENOMEM);
 	}
@@ -47,7 +48,8 @@ struct i915_page_directory *alloc_pd(struct i915_address_space *vm)
 	if (unlikely(!pd))
 		return ERR_PTR(-ENOMEM);
 
-	if (unlikely(setup_page_dma(vm, px_base(pd)))) {
+	pd->pt.base = vm->alloc_pt_dma(vm, I915_GTT_PAGE_SIZE_4K);
+	if (IS_ERR(pd->pt.base)) {
 		kfree(pd);
 		return ERR_PTR(-ENOMEM);
 	}
@@ -55,27 +57,28 @@ struct i915_page_directory *alloc_pd(struct i915_address_space *vm)
 	return pd;
 }
 
-void free_pd(struct i915_address_space *vm, struct i915_page_dma *pd)
+void free_pt(struct i915_address_space *vm, struct i915_page_table *pt)
 {
-	cleanup_page_dma(vm, pd);
-	kfree(pd);
+	i915_gem_object_put(pt->base);
+	kfree(pt);
 }
 
 static inline void
-write_dma_entry(struct i915_page_dma * const pdma,
+write_dma_entry(struct drm_i915_gem_object * const pdma,
 		const unsigned short idx,
 		const u64 encoded_entry)
 {
-	u64 * const vaddr = kmap_atomic(pdma->page);
+	u64 * const vaddr = kmap_atomic(__px_page(pdma));
 
 	vaddr[idx] = encoded_entry;
+	clflush_cache_range(&vaddr[idx], sizeof(u64));
 	kunmap_atomic(vaddr);
 }
 
 void
 __set_pd_entry(struct i915_page_directory * const pd,
 	       const unsigned short idx,
-	       struct i915_page_dma * const to,
+	       struct i915_page_table * const to,
 	       u64 (*encode)(const dma_addr_t, const enum i915_cache_level))
 {
 	/* Each thread pre-pins the pd, and we may have a thread per pde. */
@@ -83,13 +86,13 @@ __set_pd_entry(struct i915_page_directory * const pd,
 
 	atomic_inc(px_used(pd));
 	pd->entry[idx] = to;
-	write_dma_entry(px_base(pd), idx, encode(to->daddr, I915_CACHE_LLC));
+	write_dma_entry(px_base(pd), idx, encode(px_dma(to), I915_CACHE_LLC));
 }
 
 void
 clear_pd_entry(struct i915_page_directory * const pd,
 	       const unsigned short idx,
-	       const struct i915_page_scratch * const scratch)
+	       const struct drm_i915_gem_object * const scratch)
 {
 	GEM_BUG_ON(atomic_read(px_used(pd)) == 0);
 
@@ -102,7 +105,7 @@ bool
 release_pd_entry(struct i915_page_directory * const pd,
 		 const unsigned short idx,
 		 struct i915_page_table * const pt,
-		 const struct i915_page_scratch * const scratch)
+		 const struct drm_i915_gem_object * const scratch)
 {
 	bool free = false;
 
@@ -231,6 +234,23 @@ int i915_vm_alloc_pt_stash(struct i915_address_space *vm,
 	return 0;
 }
 
+int i915_vm_pin_pt_stash(struct i915_address_space *vm,
+			 struct i915_vm_pt_stash *stash)
+{
+	struct i915_page_table *pt;
+	int n, err;
+
+	for (n = 0; n < ARRAY_SIZE(stash->pt); n++) {
+		for (pt = stash->pt[n]; pt; pt = pt->stash) {
+			err = pin_pt_dma(vm, pt->base);
+			if (err)
+				return err;
+		}
+	}
+
+	return 0;
+}
+
 void i915_vm_free_pt_stash(struct i915_address_space *vm,
 			   struct i915_vm_pt_stash *stash)
 {
diff --git a/drivers/gpu/drm/i915/gt/intel_ring_submission.c b/drivers/gpu/drm/i915/gt/intel_ring_submission.c
index 68a08486fc87..f1f27b7fc746 100644
--- a/drivers/gpu/drm/i915/gt/intel_ring_submission.c
+++ b/drivers/gpu/drm/i915/gt/intel_ring_submission.c
@@ -201,16 +201,18 @@ static struct i915_address_space *vm_alias(struct i915_address_space *vm)
 	return vm;
 }
 
+static u32 pp_dir(struct i915_address_space *vm)
+{
+	return to_gen6_ppgtt(i915_vm_to_ppgtt(vm))->pp_dir;
+}
+
 static void set_pp_dir(struct intel_engine_cs *engine)
 {
 	struct i915_address_space *vm = vm_alias(engine->gt->vm);
 
 	if (vm) {
-		struct i915_ppgtt *ppgtt = i915_vm_to_ppgtt(vm);
-
 		ENGINE_WRITE(engine, RING_PP_DIR_DCLV, PP_DIR_DCLV_2G);
-		ENGINE_WRITE(engine, RING_PP_DIR_BASE,
-			     px_base(ppgtt->pd)->ggtt_offset << 10);
+		ENGINE_WRITE(engine, RING_PP_DIR_BASE, pp_dir(vm));
 	}
 }
 
@@ -608,7 +610,7 @@ static const struct intel_context_ops ring_context_ops = {
 };
 
 static int load_pd_dir(struct i915_request *rq,
-		       const struct i915_ppgtt *ppgtt,
+		       struct i915_address_space *vm,
 		       u32 valid)
 {
 	const struct intel_engine_cs * const engine = rq->engine;
@@ -624,7 +626,7 @@ static int load_pd_dir(struct i915_request *rq,
 
 	*cs++ = MI_LOAD_REGISTER_IMM(1);
 	*cs++ = i915_mmio_reg_offset(RING_PP_DIR_BASE(engine->mmio_base));
-	*cs++ = px_base(ppgtt->pd)->ggtt_offset << 10;
+	*cs++ = pp_dir(vm);
 
 	/* Stall until the page table load is complete? */
 	*cs++ = MI_STORE_REGISTER_MEM | MI_SRM_LRM_GLOBAL_GTT;
@@ -826,7 +828,7 @@ static int switch_mm(struct i915_request *rq, struct i915_address_space *vm)
 	 * post-sync op, this extra pass appears vital before a
 	 * mm switch!
 	 */
-	ret = load_pd_dir(rq, i915_vm_to_ppgtt(vm), PP_DIR_DCLV_2G);
+	ret = load_pd_dir(rq, vm, PP_DIR_DCLV_2G);
 	if (ret)
 		return ret;
 
diff --git a/drivers/gpu/drm/i915/gvt/scheduler.c b/drivers/gpu/drm/i915/gvt/scheduler.c
index 3c3b9842bbbd..1570eb8aa978 100644
--- a/drivers/gpu/drm/i915/gvt/scheduler.c
+++ b/drivers/gpu/drm/i915/gvt/scheduler.c
@@ -403,6 +403,14 @@ static void release_shadow_wa_ctx(struct intel_shadow_wa_ctx *wa_ctx)
 	wa_ctx->indirect_ctx.shadow_va = NULL;
 }
 
+static void set_dma_address(struct i915_page_directory *pd, dma_addr_t addr)
+{
+	struct scatterlist *sg = pd->pt.base->mm.pages->sgl;
+
+	/* This is not a good idea */
+	sg->dma_address = addr;
+}
+
 static void set_context_ppgtt_from_shadow(struct intel_vgpu_workload *workload,
 					  struct intel_context *ce)
 {
@@ -411,7 +419,7 @@ static void set_context_ppgtt_from_shadow(struct intel_vgpu_workload *workload,
 	int i = 0;
 
 	if (mm->ppgtt_mm.root_entry_type == GTT_TYPE_PPGTT_ROOT_L4_ENTRY) {
-		px_dma(ppgtt->pd) = mm->ppgtt_mm.shadow_pdps[0];
+		set_dma_address(ppgtt->pd, mm->ppgtt_mm.shadow_pdps[0]);
 	} else {
 		for (i = 0; i < GVT_RING_CTX_NR_PDPS; i++) {
 			struct i915_page_directory * const pd =
@@ -421,7 +429,8 @@ static void set_context_ppgtt_from_shadow(struct intel_vgpu_workload *workload,
 			   shadow ppgtt. */
 			if (!pd)
 				break;
-			px_dma(pd) = mm->ppgtt_mm.shadow_pdps[i];
+
+			set_dma_address(pd, mm->ppgtt_mm.shadow_pdps[i]);
 		}
 	}
 }
@@ -1240,13 +1249,13 @@ i915_context_ppgtt_root_restore(struct intel_vgpu_submission *s,
 	int i;
 
 	if (i915_vm_is_4lvl(&ppgtt->vm)) {
-		px_dma(ppgtt->pd) = s->i915_context_pml4;
+		set_dma_address(ppgtt->pd, s->i915_context_pml4);
 	} else {
 		for (i = 0; i < GEN8_3LVL_PDPES; i++) {
 			struct i915_page_directory * const pd =
 				i915_pd_entry(ppgtt->pd, i);
 
-			px_dma(pd) = s->i915_context_pdps[i];
+			set_dma_address(pd, s->i915_context_pdps[i]);
 		}
 	}
 }
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index 67102dc26fce..ea281d7b0630 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -1080,6 +1080,7 @@ static void i915_driver_release(struct drm_device *dev)
 
 	intel_memory_regions_driver_release(dev_priv);
 	i915_ggtt_driver_release(dev_priv);
+	i915_gem_drain_freed_objects(dev_priv);
 
 	i915_driver_mmio_release(dev_priv);
 
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 2c2e88d49f3e..0df73a14af1c 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -590,11 +590,6 @@ struct i915_gem_mm {
 	 */
 	atomic_t free_count;
 
-	/**
-	 * Small stash of WC pages
-	 */
-	struct pagestash wc_stash;
-
 	/**
 	 * tmpfs instance used for shmem backed objects
 	 */
diff --git a/drivers/gpu/drm/i915/i915_vma.c b/drivers/gpu/drm/i915/i915_vma.c
index d15a8e7d514a..3b43d485d7c2 100644
--- a/drivers/gpu/drm/i915/i915_vma.c
+++ b/drivers/gpu/drm/i915/i915_vma.c
@@ -872,13 +872,16 @@ int i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
 	if (err)
 		return err;
 
+	if (flags & PIN_GLOBAL)
+		wakeref = intel_runtime_pm_get(&vma->vm->i915->runtime_pm);
+
 	if (flags & vma->vm->bind_async_flags) {
 		u64 max_size;
 
 		work = i915_vma_work();
 		if (!work) {
 			err = -ENOMEM;
-			goto err_pages;
+			goto err_rpm;
 		}
 
 		work->vm = i915_vm_get(vma->vm);
@@ -887,11 +890,14 @@ int i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
 		max_size = max(size, vma->size);
 		if (flags & PIN_MAPPABLE)
 			max_size = max_t(u64, max_size, vma->fence_size);
-		i915_vm_alloc_pt_stash(vma->vm, &work->stash, max_size);
-	}
+		err = i915_vm_alloc_pt_stash(vma->vm, &work->stash, max_size);
+		if (err)
+			goto err_fence;
 
-	if (flags & PIN_GLOBAL)
-		wakeref = intel_runtime_pm_get(&vma->vm->i915->runtime_pm);
+		err = i915_vm_pin_pt_stash(vma->vm, &work->stash);
+		if (err)
+			goto err_fence;
+	}
 
 	/*
 	 * Differentiate between user/kernel vma inside the aliasing-ppgtt.
@@ -980,9 +986,9 @@ int i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
 err_fence:
 	if (work)
 		dma_fence_work_commit_imm(&work->base);
+err_rpm:
 	if (wakeref)
 		intel_runtime_pm_put(&vma->vm->i915->runtime_pm, wakeref);
-err_pages:
 	vma_put_pages(vma);
 	return err;
 }
diff --git a/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c b/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
index 9b8fc990e9ef..af8205a2bd8f 100644
--- a/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
+++ b/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
@@ -178,6 +178,12 @@ static int igt_ppgtt_alloc(void *arg)
 		if (err)
 			goto err_ppgtt_cleanup;
 
+		err = i915_vm_pin_pt_stash(&ppgtt->vm, &stash);
+		if (err) {
+			i915_vm_free_pt_stash(&ppgtt->vm, &stash);
+			goto err_ppgtt_cleanup;
+		}
+
 		ppgtt->vm.allocate_va_range(&ppgtt->vm, &stash, 0, size);
 		cond_resched();
 
@@ -194,6 +200,12 @@ static int igt_ppgtt_alloc(void *arg)
 		if (err)
 			goto err_ppgtt_cleanup;
 
+		err = i915_vm_pin_pt_stash(&ppgtt->vm, &stash);
+		if (err) {
+			i915_vm_free_pt_stash(&ppgtt->vm, &stash);
+			goto err_ppgtt_cleanup;
+		}
+
 		ppgtt->vm.allocate_va_range(&ppgtt->vm, &stash,
 					    last, size - last);
 		cond_resched();
@@ -289,6 +301,11 @@ static int lowlevel_hole(struct i915_address_space *vm,
 							   BIT_ULL(size)))
 					break;
 
+				if (i915_vm_pin_pt_stash(vm, &stash)) {
+					i915_vm_free_pt_stash(vm, &stash);
+					break;
+				}
+
 				vm->allocate_va_range(vm, &stash,
 						      addr, BIT_ULL(size));
 
@@ -1912,6 +1929,12 @@ static int igt_cs_tlb(void *arg)
 			if (err)
 				goto end;
 
+			err = i915_vm_pin_pt_stash(vm, &stash);
+			if (err) {
+				i915_vm_free_pt_stash(vm, &stash);
+				goto end;
+			}
+
 			vm->allocate_va_range(vm, &stash, offset, chunk_size);
 
 			i915_vm_free_pt_stash(vm, &stash);
diff --git a/drivers/gpu/drm/i915/selftests/mock_gtt.c b/drivers/gpu/drm/i915/selftests/mock_gtt.c
index 5e4fb0fba34b..7270fc8ca801 100644
--- a/drivers/gpu/drm/i915/selftests/mock_gtt.c
+++ b/drivers/gpu/drm/i915/selftests/mock_gtt.c
@@ -78,6 +78,8 @@ struct i915_ppgtt *mock_ppgtt(struct drm_i915_private *i915, const char *name)
 
 	i915_address_space_init(&ppgtt->vm, VM_CLASS_PPGTT);
 
+	ppgtt->vm.alloc_pt_dma = alloc_pt_dma;
+
 	ppgtt->vm.clear_range = mock_clear_range;
 	ppgtt->vm.insert_page = mock_insert_page;
 	ppgtt->vm.insert_entries = mock_insert_entries;
@@ -116,6 +118,8 @@ void mock_init_ggtt(struct drm_i915_private *i915, struct i915_ggtt *ggtt)
 	ggtt->mappable_end = resource_size(&ggtt->gmadr);
 	ggtt->vm.total = 4096 * PAGE_SIZE;
 
+	ggtt->vm.alloc_pt_dma = alloc_pt_dma;
+
 	ggtt->vm.clear_range = mock_clear_range;
 	ggtt->vm.insert_page = mock_insert_page;
 	ggtt->vm.insert_entries = mock_insert_entries;
-- 
2.20.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [Intel-gfx] [PATCH 03/20] drm/i915/gem: Don't drop the timeline lock during execbuf
  2020-07-06  6:19 [Intel-gfx] s/obj->mm.lock// Chris Wilson
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 01/20] drm/i915: Preallocate stashes for vma page-directories Chris Wilson
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 02/20] drm/i915: Switch to object allocations for page directories Chris Wilson
@ 2020-07-06  6:19 ` Chris Wilson
  2020-07-08 16:54   ` Tvrtko Ursulin
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 04/20] drm/i915/gem: Rename execbuf.bind_link to unbound_link Chris Wilson
                   ` (21 subsequent siblings)
  24 siblings, 1 reply; 65+ messages in thread
From: Chris Wilson @ 2020-07-06  6:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: Chris Wilson

Our timeline lock is our defence against a concurrent execbuf
interrupting our request construction. we need hold it throughout or,
for example, a second thread may interject a relocation request in
between our own relocation request and execution in the ring.

A second, major benefit, is that it allows us to preserve a large chunk
of the ringbuffer for our exclusive use; which should virtually
eliminate the threat of hitting a wait_for_space during request
construction -- although we should have already dropped other
contentious locks at that point.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 .../gpu/drm/i915/gem/i915_gem_execbuffer.c    | 241 ++++++++++++------
 .../i915/gem/selftests/i915_gem_execbuffer.c  |  24 +-
 2 files changed, 186 insertions(+), 79 deletions(-)

diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
index dd15a799f9d6..e4d06db3f313 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
@@ -259,6 +259,8 @@ struct i915_execbuffer {
 		bool has_fence : 1;
 		bool needs_unfenced : 1;
 
+		struct intel_context *ce;
+
 		struct i915_vma *target;
 		struct i915_request *rq;
 		struct i915_vma *rq_vma;
@@ -639,6 +641,35 @@ static int eb_reserve_vma(const struct i915_execbuffer *eb,
 	return 0;
 }
 
+static void retire_requests(struct intel_timeline *tl, struct i915_request *end)
+{
+	struct i915_request *rq, *rn;
+
+	list_for_each_entry_safe(rq, rn, &tl->requests, link)
+		if (rq == end || !i915_request_retire(rq))
+			break;
+}
+
+static int wait_for_timeline(struct intel_timeline *tl)
+{
+	do {
+		struct dma_fence *fence;
+		int err;
+
+		fence = i915_active_fence_get(&tl->last_request);
+		if (!fence)
+			return 0;
+
+		err = dma_fence_wait(fence, true);
+		dma_fence_put(fence);
+		if (err)
+			return err;
+
+		/* Retiring may trigger a barrier, requiring an extra pass */
+		retire_requests(tl, NULL);
+	} while (1);
+}
+
 static int eb_reserve(struct i915_execbuffer *eb)
 {
 	const unsigned int count = eb->buffer_count;
@@ -646,7 +677,6 @@ static int eb_reserve(struct i915_execbuffer *eb)
 	struct list_head last;
 	struct eb_vma *ev;
 	unsigned int i, pass;
-	int err = 0;
 
 	/*
 	 * Attempt to pin all of the buffers into the GTT.
@@ -662,18 +692,22 @@ static int eb_reserve(struct i915_execbuffer *eb)
 	 * room for the earlier objects *unless* we need to defragment.
 	 */
 
-	if (mutex_lock_interruptible(&eb->i915->drm.struct_mutex))
-		return -EINTR;
-
 	pass = 0;
 	do {
+		int err = 0;
+
+		if (mutex_lock_interruptible(&eb->i915->drm.struct_mutex))
+			return -EINTR;
+
 		list_for_each_entry(ev, &eb->unbound, bind_link) {
 			err = eb_reserve_vma(eb, ev, pin_flags);
 			if (err)
 				break;
 		}
-		if (!(err == -ENOSPC || err == -EAGAIN))
-			break;
+		if (!(err == -ENOSPC || err == -EAGAIN)) {
+			mutex_unlock(&eb->i915->drm.struct_mutex);
+			return err;
+		}
 
 		/* Resort *all* the objects into priority order */
 		INIT_LIST_HEAD(&eb->unbound);
@@ -702,11 +736,10 @@ static int eb_reserve(struct i915_execbuffer *eb)
 				list_add_tail(&ev->bind_link, &last);
 		}
 		list_splice_tail(&last, &eb->unbound);
+		mutex_unlock(&eb->i915->drm.struct_mutex);
 
 		if (err == -EAGAIN) {
-			mutex_unlock(&eb->i915->drm.struct_mutex);
 			flush_workqueue(eb->i915->mm.userptr_wq);
-			mutex_lock(&eb->i915->drm.struct_mutex);
 			continue;
 		}
 
@@ -715,25 +748,23 @@ static int eb_reserve(struct i915_execbuffer *eb)
 			break;
 
 		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);
+			/*
+			 * Too fragmented, retire everything on the timeline
+			 * and so make it all [contexts included] available to
+			 * evict.
+			 */
+			err = wait_for_timeline(eb->context->timeline);
 			if (err)
-				goto unlock;
+				return err;
+
 			break;
 
 		default:
-			err = -ENOSPC;
-			goto unlock;
+			return -ENOSPC;
 		}
 
 		pin_flags = PIN_USER;
 	} while (1);
-
-unlock:
-	mutex_unlock(&eb->i915->drm.struct_mutex);
-	return err;
 }
 
 static unsigned int eb_batch_index(const struct i915_execbuffer *eb)
@@ -1007,13 +1038,44 @@ static int reloc_gpu_chain(struct reloc_cache *cache)
 	return err;
 }
 
+static struct i915_request *
+nested_request_create(struct intel_context *ce)
+{
+	struct i915_request *rq;
+
+	/* XXX This only works once; replace with shared timeline */
+	mutex_lock_nested(&ce->timeline->mutex, SINGLE_DEPTH_NESTING);
+	intel_context_enter(ce);
+
+	rq = __i915_request_create(ce, GFP_KERNEL);
+
+	intel_context_exit(ce);
+	if (IS_ERR(rq))
+		mutex_unlock(&ce->timeline->mutex);
+
+	return rq;
+}
+
+static void __i915_request_add(struct i915_request *rq,
+			       struct i915_sched_attr *attr)
+{
+	struct intel_timeline * const tl = i915_request_timeline(rq);
+
+	lockdep_assert_held(&tl->mutex);
+	lockdep_unpin_lock(&tl->mutex, rq->cookie);
+
+	__i915_request_commit(rq);
+	__i915_request_queue(rq, attr);
+}
+
 static unsigned int reloc_bb_flags(const struct reloc_cache *cache)
 {
 	return cache->gen > 5 ? 0 : I915_DISPATCH_SECURE;
 }
 
-static int reloc_gpu_flush(struct reloc_cache *cache)
+static int reloc_gpu_flush(struct i915_execbuffer *eb)
 {
+	struct reloc_cache *cache = &eb->reloc_cache;
 	struct i915_request *rq;
 	int err;
 
@@ -1044,7 +1106,9 @@ static int reloc_gpu_flush(struct reloc_cache *cache)
 		i915_request_set_error_once(rq, err);
 
 	intel_gt_chipset_flush(rq->engine->gt);
-	i915_request_add(rq);
+	__i915_request_add(rq, &eb->gem_context->sched);
+	if (i915_request_timeline(rq) != eb->context->timeline)
+		mutex_unlock(&i915_request_timeline(rq)->mutex);
 
 	return err;
 }
@@ -1103,27 +1167,15 @@ static int __reloc_gpu_alloc(struct i915_execbuffer *eb,
 	if (err)
 		goto err_unmap;
 
-	if (engine == eb->context->engine) {
-		rq = i915_request_create(eb->context);
-	} else {
-		struct intel_context *ce;
-
-		ce = intel_context_create(engine);
-		if (IS_ERR(ce)) {
-			err = PTR_ERR(ce);
-			goto err_unpin;
-		}
-
-		i915_vm_put(ce->vm);
-		ce->vm = i915_vm_get(eb->context->vm);
-
-		rq = intel_context_create_request(ce);
-		intel_context_put(ce);
-	}
+	if (cache->ce == eb->context)
+		rq = __i915_request_create(cache->ce, GFP_KERNEL);
+	else
+		rq = nested_request_create(cache->ce);
 	if (IS_ERR(rq)) {
 		err = PTR_ERR(rq);
 		goto err_unpin;
 	}
+	rq->cookie = lockdep_pin_lock(&i915_request_timeline(rq)->mutex);
 
 	err = intel_gt_buffer_pool_mark_active(pool, rq);
 	if (err)
@@ -1151,7 +1203,9 @@ static int __reloc_gpu_alloc(struct i915_execbuffer *eb,
 skip_request:
 	i915_request_set_error_once(rq, err);
 err_request:
-	i915_request_add(rq);
+	__i915_request_add(rq, &eb->gem_context->sched);
+	if (i915_request_timeline(rq) != eb->context->timeline)
+		mutex_unlock(&i915_request_timeline(rq)->mutex);
 err_unpin:
 	i915_vma_unpin(batch);
 err_unmap:
@@ -1161,11 +1215,6 @@ static int __reloc_gpu_alloc(struct i915_execbuffer *eb,
 	return err;
 }
 
-static bool reloc_can_use_engine(const struct intel_engine_cs *engine)
-{
-	return engine->class != VIDEO_DECODE_CLASS || !IS_GEN(engine->i915, 6);
-}
-
 static u32 *reloc_gpu(struct i915_execbuffer *eb,
 		      struct i915_vma *vma,
 		      unsigned int len)
@@ -1177,12 +1226,6 @@ static u32 *reloc_gpu(struct i915_execbuffer *eb,
 	if (unlikely(!cache->rq)) {
 		struct intel_engine_cs *engine = eb->engine;
 
-		if (!reloc_can_use_engine(engine)) {
-			engine = engine->gt->engine_class[COPY_ENGINE_CLASS][0];
-			if (!engine)
-				return ERR_PTR(-ENODEV);
-		}
-
 		err = __reloc_gpu_alloc(eb, engine, len);
 		if (unlikely(err))
 			return ERR_PTR(err);
@@ -1513,7 +1556,7 @@ static int eb_relocate(struct i915_execbuffer *eb)
 				break;
 		}
 
-		flush = reloc_gpu_flush(&eb->reloc_cache);
+		flush = reloc_gpu_flush(eb);
 		if (!err)
 			err = flush;
 	}
@@ -1737,21 +1780,17 @@ parser_mark_active(struct eb_parse_work *pw, struct intel_timeline *tl)
 {
 	int err;
 
-	mutex_lock(&tl->mutex);
-
 	err = __parser_mark_active(pw->shadow, tl, &pw->base.dma);
 	if (err)
-		goto unlock;
+		return err;
 
 	if (pw->trampoline) {
 		err = __parser_mark_active(pw->trampoline, tl, &pw->base.dma);
 		if (err)
-			goto unlock;
+			return err;
 	}
 
-unlock:
-	mutex_unlock(&tl->mutex);
-	return err;
+	return 0;
 }
 
 static int eb_parse_pipeline(struct i915_execbuffer *eb,
@@ -2044,6 +2083,54 @@ static struct i915_request *eb_throttle(struct intel_context *ce)
 	return i915_request_get(rq);
 }
 
+static bool reloc_can_use_engine(const struct intel_engine_cs *engine)
+{
+	return engine->class != VIDEO_DECODE_CLASS || !IS_GEN(engine->i915, 6);
+}
+
+static int __eb_pin_reloc_engine(struct i915_execbuffer *eb)
+{
+	struct intel_engine_cs *engine = eb->engine;
+	struct intel_context *ce;
+	int err;
+
+	if (reloc_can_use_engine(engine)) {
+		eb->reloc_cache.ce = eb->context;
+		return 0;
+	}
+
+	engine = engine->gt->engine_class[COPY_ENGINE_CLASS][0];
+	if (!engine)
+		return -ENODEV;
+
+	ce = intel_context_create(engine);
+	if (IS_ERR(ce))
+		return PTR_ERR(ce);
+
+	/* Reuse eb->context->timeline with scheduler! */
+
+	i915_vm_put(ce->vm);
+	ce->vm = i915_vm_get(eb->context->vm);
+
+	err = intel_context_pin(ce);
+	if (err)
+		return err;
+
+	eb->reloc_cache.ce = ce;
+	return 0;
+}
+
+static void __eb_unpin_reloc_engine(struct i915_execbuffer *eb)
+{
+	struct intel_context *ce = eb->reloc_cache.ce;
+
+	if (ce == eb->context)
+		return;
+
+	intel_context_unpin(ce);
+	intel_context_put(ce);
+}
+
 static int __eb_pin_engine(struct i915_execbuffer *eb, struct intel_context *ce)
 {
 	struct intel_timeline *tl;
@@ -2087,9 +2174,7 @@ static int __eb_pin_engine(struct i915_execbuffer *eb, struct intel_context *ce)
 	intel_context_enter(ce);
 	rq = eb_throttle(ce);
 
-	intel_context_timeline_unlock(tl);
-
-	if (rq) {
+	while (rq) {
 		bool nonblock = eb->file->filp->f_flags & O_NONBLOCK;
 		long timeout;
 
@@ -2097,23 +2182,34 @@ static int __eb_pin_engine(struct i915_execbuffer *eb, struct intel_context *ce)
 		if (nonblock)
 			timeout = 0;
 
+		mutex_unlock(&tl->mutex);
+
 		timeout = i915_request_wait(rq,
 					    I915_WAIT_INTERRUPTIBLE,
 					    timeout);
 		i915_request_put(rq);
 
+		mutex_lock(&tl->mutex);
+
 		if (timeout < 0) {
 			err = nonblock ? -EWOULDBLOCK : timeout;
 			goto err_exit;
 		}
+
+		retire_requests(tl, NULL);
+		rq = eb_throttle(ce);
 	}
 
 	eb->engine = ce->engine;
 	eb->context = ce;
+
+	err = __eb_pin_reloc_engine(eb);
+	if (err)
+		goto err_exit;
+
 	return 0;
 
 err_exit:
-	mutex_lock(&tl->mutex);
 	intel_context_exit(ce);
 	intel_context_timeline_unlock(tl);
 err_unpin:
@@ -2124,11 +2220,11 @@ static int __eb_pin_engine(struct i915_execbuffer *eb, struct intel_context *ce)
 static void eb_unpin_engine(struct i915_execbuffer *eb)
 {
 	struct intel_context *ce = eb->context;
-	struct intel_timeline *tl = ce->timeline;
 
-	mutex_lock(&tl->mutex);
+	__eb_unpin_reloc_engine(eb);
+
 	intel_context_exit(ce);
-	mutex_unlock(&tl->mutex);
+	intel_context_timeline_unlock(ce->timeline);
 
 	intel_context_unpin(ce);
 }
@@ -2330,15 +2426,6 @@ signal_fence_array(struct i915_execbuffer *eb,
 	}
 }
 
-static void retire_requests(struct intel_timeline *tl, struct i915_request *end)
-{
-	struct i915_request *rq, *rn;
-
-	list_for_each_entry_safe(rq, rn, &tl->requests, link)
-		if (rq == end || !i915_request_retire(rq))
-			break;
-}
-
 static void eb_request_add(struct i915_execbuffer *eb)
 {
 	struct i915_request *rq = eb->request;
@@ -2367,8 +2454,6 @@ static void eb_request_add(struct i915_execbuffer *eb)
 	/* Try to clean up the client's timeline after submitting the request */
 	if (prev)
 		retire_requests(tl, prev);
-
-	mutex_unlock(&tl->mutex);
 }
 
 static int
@@ -2455,6 +2540,7 @@ i915_gem_do_execbuffer(struct drm_device *dev,
 	err = eb_pin_engine(&eb, file, args);
 	if (unlikely(err))
 		goto err_context;
+	lockdep_assert_held(&eb.context->timeline->mutex);
 
 	err = eb_relocate(&eb);
 	if (err) {
@@ -2522,11 +2608,12 @@ i915_gem_do_execbuffer(struct drm_device *dev,
 	GEM_BUG_ON(eb.reloc_cache.rq);
 
 	/* Allocate a request for this batch buffer nice and early. */
-	eb.request = i915_request_create(eb.context);
+	eb.request = __i915_request_create(eb.context, GFP_KERNEL);
 	if (IS_ERR(eb.request)) {
 		err = PTR_ERR(eb.request);
 		goto err_batch_unpin;
 	}
+	eb.request->cookie = lockdep_pin_lock(&eb.context->timeline->mutex);
 
 	if (in_fence) {
 		if (args->flags & I915_EXEC_FENCE_SUBMIT)
diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_execbuffer.c
index 57c14d3340cd..992d46db1b33 100644
--- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_execbuffer.c
@@ -7,6 +7,9 @@
 
 #include "gt/intel_engine_pm.h"
 #include "selftests/igt_flush_test.h"
+#include "selftests/mock_drm.h"
+
+#include "mock_context.h"
 
 static u64 read_reloc(const u32 *map, int x, const u64 mask)
 {
@@ -60,7 +63,7 @@ static int __igt_gpu_reloc(struct i915_execbuffer *eb,
 
 	GEM_BUG_ON(!eb->reloc_cache.rq);
 	rq = i915_request_get(eb->reloc_cache.rq);
-	err = reloc_gpu_flush(&eb->reloc_cache);
+	err = reloc_gpu_flush(eb);
 	if (err)
 		goto put_rq;
 	GEM_BUG_ON(eb->reloc_cache.rq);
@@ -100,14 +103,22 @@ static int igt_gpu_reloc(void *arg)
 {
 	struct i915_execbuffer eb;
 	struct drm_i915_gem_object *scratch;
+	struct file *file;
 	int err = 0;
 	u32 *map;
 
+	file = mock_file(arg);
+	if (IS_ERR(file))
+		return PTR_ERR(file);
+
 	eb.i915 = arg;
+	eb.gem_context = live_context(arg, file);
+	if (IS_ERR(eb.gem_context))
+		goto err_file;
 
 	scratch = i915_gem_object_create_internal(eb.i915, 4096);
 	if (IS_ERR(scratch))
-		return PTR_ERR(scratch);
+		goto err_file;
 
 	map = i915_gem_object_pin_map(scratch, I915_MAP_WC);
 	if (IS_ERR(map)) {
@@ -130,8 +141,15 @@ static int igt_gpu_reloc(void *arg)
 		if (err)
 			goto err_put;
 
+		mutex_lock(&eb.context->timeline->mutex);
+		intel_context_enter(eb.context);
+		eb.reloc_cache.ce = eb.context;
+
 		err = __igt_gpu_reloc(&eb, scratch);
 
+		intel_context_exit(eb.context);
+		mutex_unlock(&eb.context->timeline->mutex);
+
 		intel_context_unpin(eb.context);
 err_put:
 		intel_context_put(eb.context);
@@ -146,6 +164,8 @@ static int igt_gpu_reloc(void *arg)
 
 err_scratch:
 	i915_gem_object_put(scratch);
+err_file:
+	fput(file);
 	return err;
 }
 
-- 
2.20.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [Intel-gfx] [PATCH 04/20] drm/i915/gem: Rename execbuf.bind_link to unbound_link
  2020-07-06  6:19 [Intel-gfx] s/obj->mm.lock// Chris Wilson
                   ` (2 preceding siblings ...)
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 03/20] drm/i915/gem: Don't drop the timeline lock during execbuf Chris Wilson
@ 2020-07-06  6:19 ` Chris Wilson
  2020-07-10 11:26   ` Tvrtko Ursulin
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 05/20] drm/i915/gem: Break apart the early i915_vma_pin from execbuf object lookup Chris Wilson
                   ` (20 subsequent siblings)
  24 siblings, 1 reply; 65+ messages in thread
From: Chris Wilson @ 2020-07-06  6:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: Chris Wilson

Rename the current list of unbound objects so that we can track of all
objects that we need to bind, as well as the list of currently unbound
[unprocessed] objects.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
index e4d06db3f313..bf8193d9e279 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
@@ -33,7 +33,7 @@ struct eb_vma {
 
 	/** This vma's place in the execbuf reservation list */
 	struct drm_i915_gem_exec_object2 *exec;
-	struct list_head bind_link;
+	struct list_head unbound_link;
 	struct list_head reloc_link;
 
 	struct hlist_node node;
@@ -594,7 +594,7 @@ eb_add_vma(struct i915_execbuffer *eb,
 		}
 	} else {
 		eb_unreserve_vma(ev);
-		list_add_tail(&ev->bind_link, &eb->unbound);
+		list_add_tail(&ev->unbound_link, &eb->unbound);
 	}
 }
 
@@ -699,7 +699,7 @@ static int eb_reserve(struct i915_execbuffer *eb)
 		if (mutex_lock_interruptible(&eb->i915->drm.struct_mutex))
 			return -EINTR;
 
-		list_for_each_entry(ev, &eb->unbound, bind_link) {
+		list_for_each_entry(ev, &eb->unbound, unbound_link) {
 			err = eb_reserve_vma(eb, ev, pin_flags);
 			if (err)
 				break;
@@ -725,15 +725,15 @@ static int eb_reserve(struct i915_execbuffer *eb)
 
 			if (flags & EXEC_OBJECT_PINNED)
 				/* Pinned must have their slot */
-				list_add(&ev->bind_link, &eb->unbound);
+				list_add(&ev->unbound_link, &eb->unbound);
 			else if (flags & __EXEC_OBJECT_NEEDS_MAP)
 				/* Map require the lowest 256MiB (aperture) */
-				list_add_tail(&ev->bind_link, &eb->unbound);
+				list_add_tail(&ev->unbound_link, &eb->unbound);
 			else if (!(flags & EXEC_OBJECT_SUPPORTS_48B_ADDRESS))
 				/* Prioritise 4GiB region for restricted bo */
-				list_add(&ev->bind_link, &last);
+				list_add(&ev->unbound_link, &last);
 			else
-				list_add_tail(&ev->bind_link, &last);
+				list_add_tail(&ev->unbound_link, &last);
 		}
 		list_splice_tail(&last, &eb->unbound);
 		mutex_unlock(&eb->i915->drm.struct_mutex);
-- 
2.20.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [Intel-gfx] [PATCH 05/20] drm/i915/gem: Break apart the early i915_vma_pin from execbuf object lookup
  2020-07-06  6:19 [Intel-gfx] s/obj->mm.lock// Chris Wilson
                   ` (3 preceding siblings ...)
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 04/20] drm/i915/gem: Rename execbuf.bind_link to unbound_link Chris Wilson
@ 2020-07-06  6:19 ` Chris Wilson
  2020-07-10 11:27   ` Tvrtko Ursulin
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 06/20] drm/i915/gem: Remove the call for no-evict i915_vma_pin Chris Wilson
                   ` (19 subsequent siblings)
  24 siblings, 1 reply; 65+ messages in thread
From: Chris Wilson @ 2020-07-06  6:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: Chris Wilson

As a prelude to the next step where we want to perform all the object
allocations together under the same lock, we first must delay the
i915_vma_pin() as that implicitly does the allocations for us, one by
one. As it only does the allocations one by one, it is not allowed to
wait/evict, whereas pulling all the allocations together the entire set
can be scheduled as one.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 .../gpu/drm/i915/gem/i915_gem_execbuffer.c    | 70 +++++++++++--------
 1 file changed, 39 insertions(+), 31 deletions(-)

diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
index bf8193d9e279..35a57c1fc9c3 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
@@ -33,6 +33,8 @@ struct eb_vma {
 
 	/** This vma's place in the execbuf reservation list */
 	struct drm_i915_gem_exec_object2 *exec;
+
+	struct list_head bind_link;
 	struct list_head unbound_link;
 	struct list_head reloc_link;
 
@@ -240,8 +242,8 @@ struct i915_execbuffer {
 	/** actual size of execobj[] as we may extend it for the cmdparser */
 	unsigned int buffer_count;
 
-	/** list of vma not yet bound during reservation phase */
-	struct list_head unbound;
+	/** list of all vma required to bound for this execbuf */
+	struct list_head bind_list;
 
 	/** list of vma that have execobj.relocation_count */
 	struct list_head relocs;
@@ -565,6 +567,8 @@ eb_add_vma(struct i915_execbuffer *eb,
 						    eb->lut_size)]);
 	}
 
+	list_add_tail(&ev->bind_link, &eb->bind_list);
+
 	if (entry->relocation_count)
 		list_add_tail(&ev->reloc_link, &eb->relocs);
 
@@ -586,16 +590,6 @@ eb_add_vma(struct i915_execbuffer *eb,
 
 		eb->batch = ev;
 	}
-
-	if (eb_pin_vma(eb, entry, ev)) {
-		if (entry->offset != vma->node.start) {
-			entry->offset = vma->node.start | UPDATE;
-			eb->args->flags |= __EXEC_HAS_RELOC;
-		}
-	} else {
-		eb_unreserve_vma(ev);
-		list_add_tail(&ev->unbound_link, &eb->unbound);
-	}
 }
 
 static int eb_reserve_vma(const struct i915_execbuffer *eb,
@@ -670,13 +664,31 @@ static int wait_for_timeline(struct intel_timeline *tl)
 	} while (1);
 }
 
-static int eb_reserve(struct i915_execbuffer *eb)
+static int eb_reserve_vm(struct i915_execbuffer *eb)
 {
-	const unsigned int count = eb->buffer_count;
 	unsigned int pin_flags = PIN_USER | PIN_NONBLOCK;
-	struct list_head last;
+	struct list_head last, unbound;
 	struct eb_vma *ev;
-	unsigned int i, pass;
+	unsigned int pass;
+
+	INIT_LIST_HEAD(&unbound);
+	list_for_each_entry(ev, &eb->bind_list, bind_link) {
+		struct drm_i915_gem_exec_object2 *entry = ev->exec;
+		struct i915_vma *vma = ev->vma;
+
+		if (eb_pin_vma(eb, entry, ev)) {
+			if (entry->offset != vma->node.start) {
+				entry->offset = vma->node.start | UPDATE;
+				eb->args->flags |= __EXEC_HAS_RELOC;
+			}
+		} else {
+			eb_unreserve_vma(ev);
+			list_add_tail(&ev->unbound_link, &unbound);
+		}
+	}
+
+	if (list_empty(&unbound))
+		return 0;
 
 	/*
 	 * Attempt to pin all of the buffers into the GTT.
@@ -699,7 +711,7 @@ static int eb_reserve(struct i915_execbuffer *eb)
 		if (mutex_lock_interruptible(&eb->i915->drm.struct_mutex))
 			return -EINTR;
 
-		list_for_each_entry(ev, &eb->unbound, unbound_link) {
+		list_for_each_entry(ev, &unbound, unbound_link) {
 			err = eb_reserve_vma(eb, ev, pin_flags);
 			if (err)
 				break;
@@ -710,13 +722,11 @@ static int eb_reserve(struct i915_execbuffer *eb)
 		}
 
 		/* Resort *all* the objects into priority order */
-		INIT_LIST_HEAD(&eb->unbound);
+		INIT_LIST_HEAD(&unbound);
 		INIT_LIST_HEAD(&last);
-		for (i = 0; i < count; i++) {
-			unsigned int flags;
+		list_for_each_entry(ev, &eb->bind_list, bind_link) {
+			unsigned int flags = ev->flags;
 
-			ev = &eb->vma[i];
-			flags = ev->flags;
 			if (flags & EXEC_OBJECT_PINNED &&
 			    flags & __EXEC_OBJECT_HAS_PIN)
 				continue;
@@ -725,17 +735,17 @@ static int eb_reserve(struct i915_execbuffer *eb)
 
 			if (flags & EXEC_OBJECT_PINNED)
 				/* Pinned must have their slot */
-				list_add(&ev->unbound_link, &eb->unbound);
+				list_add(&ev->unbound_link, &unbound);
 			else if (flags & __EXEC_OBJECT_NEEDS_MAP)
 				/* Map require the lowest 256MiB (aperture) */
-				list_add_tail(&ev->unbound_link, &eb->unbound);
+				list_add_tail(&ev->unbound_link, &unbound);
 			else if (!(flags & EXEC_OBJECT_SUPPORTS_48B_ADDRESS))
 				/* Prioritise 4GiB region for restricted bo */
 				list_add(&ev->unbound_link, &last);
 			else
 				list_add_tail(&ev->unbound_link, &last);
 		}
-		list_splice_tail(&last, &eb->unbound);
+		list_splice_tail(&last, &unbound);
 		mutex_unlock(&eb->i915->drm.struct_mutex);
 
 		if (err == -EAGAIN) {
@@ -891,8 +901,8 @@ static int eb_lookup_vmas(struct i915_execbuffer *eb)
 	unsigned int i;
 	int err = 0;
 
+	INIT_LIST_HEAD(&eb->bind_list);
 	INIT_LIST_HEAD(&eb->relocs);
-	INIT_LIST_HEAD(&eb->unbound);
 
 	for (i = 0; i < eb->buffer_count; i++) {
 		struct i915_vma *vma;
@@ -1539,11 +1549,9 @@ static int eb_relocate(struct i915_execbuffer *eb)
 	if (err)
 		return err;
 
-	if (!list_empty(&eb->unbound)) {
-		err = eb_reserve(eb);
-		if (err)
-			return err;
-	}
+	err = eb_reserve_vm(eb);
+	if (err)
+		return err;
 
 	/* The objects are in their final locations, apply the relocations. */
 	if (eb->args->flags & __EXEC_HAS_RELOC) {
-- 
2.20.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [Intel-gfx] [PATCH 06/20] drm/i915/gem: Remove the call for no-evict i915_vma_pin
  2020-07-06  6:19 [Intel-gfx] s/obj->mm.lock// Chris Wilson
                   ` (4 preceding siblings ...)
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 05/20] drm/i915/gem: Break apart the early i915_vma_pin from execbuf object lookup Chris Wilson
@ 2020-07-06  6:19 ` Chris Wilson
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 07/20] drm/i915: Add list_for_each_entry_safe_continue_reverse Chris Wilson
                   ` (18 subsequent siblings)
  24 siblings, 0 replies; 65+ messages in thread
From: Chris Wilson @ 2020-07-06  6:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: Chris Wilson

Remove the stub i915_vma_pin() used for incrementally pining objects for
execbuf (under the severe restriction that they must not wait on a
resource as we may have already pinned it) and replace it with a
i915_vma_pin_inplace() that is only allowed to reclaim the currently
bound location for the vma (and will never wait for a pinned resource).

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 .../gpu/drm/i915/gem/i915_gem_execbuffer.c    | 69 +++++++++++--------
 drivers/gpu/drm/i915/i915_vma.c               |  6 +-
 drivers/gpu/drm/i915/i915_vma.h               |  2 +
 3 files changed, 45 insertions(+), 32 deletions(-)

diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
index 35a57c1fc9c3..1015c4fd9f3e 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
@@ -452,49 +452,55 @@ static u64 eb_pin_flags(const struct drm_i915_gem_exec_object2 *entry,
 	return pin_flags;
 }
 
+static bool eb_pin_vma_fence_inplace(struct eb_vma *ev)
+{
+	struct i915_vma *vma = ev->vma;
+	struct i915_fence_reg *reg = vma->fence;
+
+	if (reg) {
+		if (READ_ONCE(reg->dirty))
+			return false;
+
+		atomic_inc(&reg->pin_count);
+		ev->flags |= __EXEC_OBJECT_HAS_FENCE;
+	} else {
+		if (i915_gem_object_is_tiled(vma->obj))
+			return false;
+	}
+
+	return true;
+}
+
 static inline bool
-eb_pin_vma(struct i915_execbuffer *eb,
-	   const struct drm_i915_gem_exec_object2 *entry,
-	   struct eb_vma *ev)
+eb_pin_vma_inplace(struct i915_execbuffer *eb,
+		   const struct drm_i915_gem_exec_object2 *entry,
+		   struct eb_vma *ev)
 {
 	struct i915_vma *vma = ev->vma;
-	u64 pin_flags;
+	unsigned int pin_flags;
 
-	if (vma->node.size)
-		pin_flags = vma->node.start;
-	else
-		pin_flags = entry->offset & PIN_OFFSET_MASK;
+	if (eb_vma_misplaced(entry, vma, ev->flags))
+		return false;
 
-	pin_flags |= PIN_USER | PIN_NOEVICT | PIN_OFFSET_FIXED;
+	pin_flags = PIN_USER;
 	if (unlikely(ev->flags & EXEC_OBJECT_NEEDS_GTT))
 		pin_flags |= PIN_GLOBAL;
 
 	/* Attempt to reuse the current location if available */
-	if (unlikely(i915_vma_pin(vma, 0, 0, pin_flags))) {
-		if (entry->flags & EXEC_OBJECT_PINNED)
-			return false;
-
-		/* Failing that pick any _free_ space if suitable */
-		if (unlikely(i915_vma_pin(vma,
-					  entry->pad_to_size,
-					  entry->alignment,
-					  eb_pin_flags(entry, ev->flags) |
-					  PIN_USER | PIN_NOEVICT)))
-			return false;
-	}
+	if (!i915_vma_pin_inplace(vma, pin_flags))
+		return false;
 
 	if (unlikely(ev->flags & EXEC_OBJECT_NEEDS_FENCE)) {
-		if (unlikely(i915_vma_pin_fence(vma))) {
-			i915_vma_unpin(vma);
+		if (!eb_pin_vma_fence_inplace(ev)) {
+			__i915_vma_unpin(vma);
 			return false;
 		}
-
-		if (vma->fence)
-			ev->flags |= __EXEC_OBJECT_HAS_FENCE;
 	}
 
+	GEM_BUG_ON(eb_vma_misplaced(entry, vma, ev->flags));
+
 	ev->flags |= __EXEC_OBJECT_HAS_PIN;
-	return !eb_vma_misplaced(entry, vma, ev->flags);
+	return true;
 }
 
 static int
@@ -676,14 +682,17 @@ static int eb_reserve_vm(struct i915_execbuffer *eb)
 		struct drm_i915_gem_exec_object2 *entry = ev->exec;
 		struct i915_vma *vma = ev->vma;
 
-		if (eb_pin_vma(eb, entry, ev)) {
+		if (eb_pin_vma_inplace(eb, entry, ev)) {
 			if (entry->offset != vma->node.start) {
 				entry->offset = vma->node.start | UPDATE;
 				eb->args->flags |= __EXEC_HAS_RELOC;
 			}
 		} else {
-			eb_unreserve_vma(ev);
-			list_add_tail(&ev->unbound_link, &unbound);
+			/* Lightly sort user placed objects to the fore */
+			if (ev->flags & EXEC_OBJECT_PINNED)
+				list_add(&ev->unbound_link, &unbound);
+			else
+				list_add_tail(&ev->unbound_link, &unbound);
 		}
 	}
 
diff --git a/drivers/gpu/drm/i915/i915_vma.c b/drivers/gpu/drm/i915/i915_vma.c
index 3b43d485d7c2..0c9af30fc3d6 100644
--- a/drivers/gpu/drm/i915/i915_vma.c
+++ b/drivers/gpu/drm/i915/i915_vma.c
@@ -740,11 +740,13 @@ i915_vma_detach(struct i915_vma *vma)
 	list_del(&vma->vm_link);
 }
 
-static bool try_qad_pin(struct i915_vma *vma, unsigned int flags)
+bool i915_vma_pin_inplace(struct i915_vma *vma, unsigned int flags)
 {
 	unsigned int bound;
 	bool pinned = true;
 
+	GEM_BUG_ON(flags & ~I915_VMA_BIND_MASK);
+
 	bound = atomic_read(&vma->flags);
 	do {
 		if (unlikely(flags & ~bound))
@@ -865,7 +867,7 @@ int i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
 	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))
+	if (i915_vma_pin_inplace(vma, flags & I915_VMA_BIND_MASK))
 		return 0;
 
 	err = vma_get_pages(vma);
diff --git a/drivers/gpu/drm/i915/i915_vma.h b/drivers/gpu/drm/i915/i915_vma.h
index d0d01f909548..03fea54fd573 100644
--- a/drivers/gpu/drm/i915/i915_vma.h
+++ b/drivers/gpu/drm/i915/i915_vma.h
@@ -236,6 +236,8 @@ static inline void i915_vma_unlock(struct i915_vma *vma)
 	dma_resv_unlock(vma->resv);
 }
 
+bool i915_vma_pin_inplace(struct i915_vma *vma, unsigned int flags);
+
 int __must_check
 i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags);
 int i915_ggtt_pin(struct i915_vma *vma, u32 align, unsigned int flags);
-- 
2.20.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [Intel-gfx] [PATCH 07/20] drm/i915: Add list_for_each_entry_safe_continue_reverse
  2020-07-06  6:19 [Intel-gfx] s/obj->mm.lock// Chris Wilson
                   ` (5 preceding siblings ...)
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 06/20] drm/i915/gem: Remove the call for no-evict i915_vma_pin Chris Wilson
@ 2020-07-06  6:19 ` Chris Wilson
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 08/20] drm/i915: Always defer fenced work to the worker Chris Wilson
                   ` (17 subsequent siblings)
  24 siblings, 0 replies; 65+ messages in thread
From: Chris Wilson @ 2020-07-06  6:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: Chris Wilson

One more list iterator variant, for when we want to unwind from inside
one list iterator with the intention of restarting from the current
entry as the new head of the list.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
---
 drivers/gpu/drm/i915/i915_utils.h | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/drivers/gpu/drm/i915/i915_utils.h b/drivers/gpu/drm/i915/i915_utils.h
index 03a73d2bd50d..6ebccdd12d4c 100644
--- a/drivers/gpu/drm/i915/i915_utils.h
+++ b/drivers/gpu/drm/i915/i915_utils.h
@@ -266,6 +266,12 @@ static inline int list_is_last_rcu(const struct list_head *list,
 	return READ_ONCE(list->next) == head;
 }
 
+#define list_for_each_entry_safe_continue_reverse(pos, n, head, member)	\
+	for (pos = list_prev_entry(pos, member),			\
+	     n = list_prev_entry(pos, member);				\
+	     &pos->member != (head);					\
+	     pos = n, n = list_prev_entry(n, member))
+
 /*
  * Wait until the work is finally complete, even if it tries to postpone
  * by requeueing itself. Note, that if the worker never cancels itself,
-- 
2.20.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [Intel-gfx] [PATCH 08/20] drm/i915: Always defer fenced work to the worker
  2020-07-06  6:19 [Intel-gfx] s/obj->mm.lock// Chris Wilson
                   ` (6 preceding siblings ...)
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 07/20] drm/i915: Add list_for_each_entry_safe_continue_reverse Chris Wilson
@ 2020-07-06  6:19 ` Chris Wilson
  2020-07-08 12:18   ` Tvrtko Ursulin
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 09/20] drm/i915/gem: Assign context id for async work Chris Wilson
                   ` (16 subsequent siblings)
  24 siblings, 1 reply; 65+ messages in thread
From: Chris Wilson @ 2020-07-06  6:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: Chris Wilson

Currently, if an error is raised we always call the cleanup locally
[and skip the main work callback]. However, some future users may need
to take a mutex to cleanup and so we cannot immediately execute the
cleanup as we may still be in interrupt context.

With the execute-immediate flag, for most cases this should result in
immediate cleanup of an error.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 drivers/gpu/drm/i915/i915_sw_fence_work.c | 25 +++++++++++------------
 1 file changed, 12 insertions(+), 13 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_sw_fence_work.c b/drivers/gpu/drm/i915/i915_sw_fence_work.c
index a3a81bb8f2c3..29f63ebc24e8 100644
--- a/drivers/gpu/drm/i915/i915_sw_fence_work.c
+++ b/drivers/gpu/drm/i915/i915_sw_fence_work.c
@@ -16,11 +16,14 @@ static void fence_complete(struct dma_fence_work *f)
 static void fence_work(struct work_struct *work)
 {
 	struct dma_fence_work *f = container_of(work, typeof(*f), work);
-	int err;
 
-	err = f->ops->work(f);
-	if (err)
-		dma_fence_set_error(&f->dma, err);
+	if (!f->dma.error) {
+		int err;
+
+		err = f->ops->work(f);
+		if (err)
+			dma_fence_set_error(&f->dma, err);
+	}
 
 	fence_complete(f);
 	dma_fence_put(&f->dma);
@@ -36,15 +39,11 @@ fence_notify(struct i915_sw_fence *fence, enum i915_sw_fence_notify state)
 		if (fence->error)
 			dma_fence_set_error(&f->dma, fence->error);
 
-		if (!f->dma.error) {
-			dma_fence_get(&f->dma);
-			if (test_bit(DMA_FENCE_WORK_IMM, &f->dma.flags))
-				fence_work(&f->work);
-			else
-				queue_work(system_unbound_wq, &f->work);
-		} else {
-			fence_complete(f);
-		}
+		dma_fence_get(&f->dma);
+		if (test_bit(DMA_FENCE_WORK_IMM, &f->dma.flags))
+			fence_work(&f->work);
+		else
+			queue_work(system_unbound_wq, &f->work);
 		break;
 
 	case FENCE_FREE:
-- 
2.20.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [Intel-gfx] [PATCH 09/20] drm/i915/gem: Assign context id for async work
  2020-07-06  6:19 [Intel-gfx] s/obj->mm.lock// Chris Wilson
                   ` (7 preceding siblings ...)
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 08/20] drm/i915: Always defer fenced work to the worker Chris Wilson
@ 2020-07-06  6:19 ` Chris Wilson
  2020-07-08 12:26   ` Tvrtko Ursulin
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 10/20] drm/i915: Export a preallocate variant of i915_active_acquire() Chris Wilson
                   ` (15 subsequent siblings)
  24 siblings, 1 reply; 65+ messages in thread
From: Chris Wilson @ 2020-07-06  6:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: Chris Wilson

Allocate a few dma fence context id that we can use to associate async work
[for the CPU] launched on behalf of this context. For extra fun, we allow
a configurable concurrency width.

A current example would be that we spawn an unbound worker for every
userptr get_pages. In the future, we wish to charge this work to the
context that initiated the async work and to impose concurrency limits
based on the context.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 drivers/gpu/drm/i915/gem/i915_gem_context.c       | 4 ++++
 drivers/gpu/drm/i915/gem/i915_gem_context.h       | 6 ++++++
 drivers/gpu/drm/i915/gem/i915_gem_context_types.h | 6 ++++++
 3 files changed, 16 insertions(+)

diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context.c b/drivers/gpu/drm/i915/gem/i915_gem_context.c
index 41784df51e58..bd68746327b3 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_context.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_context.c
@@ -714,6 +714,10 @@ __create_context(struct drm_i915_private *i915)
 	ctx->sched.priority = I915_USER_PRIORITY(I915_PRIORITY_NORMAL);
 	mutex_init(&ctx->mutex);
 
+	ctx->async.width = rounddown_pow_of_two(num_online_cpus());
+	ctx->async.context = dma_fence_context_alloc(ctx->async.width);
+	ctx->async.width--;
+
 	spin_lock_init(&ctx->stale.lock);
 	INIT_LIST_HEAD(&ctx->stale.engines);
 
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context.h b/drivers/gpu/drm/i915/gem/i915_gem_context.h
index 3702b2fb27ab..e104ff0ae740 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_context.h
+++ b/drivers/gpu/drm/i915/gem/i915_gem_context.h
@@ -134,6 +134,12 @@ int i915_gem_context_setparam_ioctl(struct drm_device *dev, void *data,
 int i915_gem_context_reset_stats_ioctl(struct drm_device *dev, void *data,
 				       struct drm_file *file);
 
+static inline u64 i915_gem_context_async_id(struct i915_gem_context *ctx)
+{
+	return (ctx->async.context +
+		(atomic_fetch_inc(&ctx->async.cur) & ctx->async.width));
+}
+
 static inline struct i915_gem_context *
 i915_gem_context_get(struct i915_gem_context *ctx)
 {
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context_types.h b/drivers/gpu/drm/i915/gem/i915_gem_context_types.h
index ae14ca24a11f..52561f98000f 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_context_types.h
+++ b/drivers/gpu/drm/i915/gem/i915_gem_context_types.h
@@ -85,6 +85,12 @@ struct i915_gem_context {
 
 	struct intel_timeline *timeline;
 
+	struct {
+		u64 context;
+		atomic_t cur;
+		unsigned int width;
+	} async;
+
 	/**
 	 * @vm: unique address space (GTT)
 	 *
-- 
2.20.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [Intel-gfx] [PATCH 10/20] drm/i915: Export a preallocate variant of i915_active_acquire()
  2020-07-06  6:19 [Intel-gfx] s/obj->mm.lock// Chris Wilson
                   ` (8 preceding siblings ...)
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 09/20] drm/i915/gem: Assign context id for async work Chris Wilson
@ 2020-07-06  6:19 ` Chris Wilson
  2020-07-09 14:36   ` Maarten Lankhorst
  2020-07-13 14:29   ` Tvrtko Ursulin
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 11/20] drm/i915/gem: Separate the ww_mutex walker into its own list Chris Wilson
                   ` (14 subsequent siblings)
  24 siblings, 2 replies; 65+ messages in thread
From: Chris Wilson @ 2020-07-06  6:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: Chris Wilson

Sometimes we have to be very careful not to allocate underneath a mutex
(or spinlock) and yet still want to track activity. Enter
i915_active_acquire_for_context(). This raises the activity counter on
i915_active prior to use and ensures that the fence-tree contains a slot
for the context.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 .../gpu/drm/i915/gem/i915_gem_execbuffer.c    |   2 +-
 drivers/gpu/drm/i915/gt/intel_timeline.c      |   4 +-
 drivers/gpu/drm/i915/i915_active.c            | 113 +++++++++++++++---
 drivers/gpu/drm/i915/i915_active.h            |  14 ++-
 4 files changed, 113 insertions(+), 20 deletions(-)

diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
index 1015c4fd9f3e..6d20be29ff3c 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
@@ -1789,7 +1789,7 @@ __parser_mark_active(struct i915_vma *vma,
 {
 	struct intel_gt_buffer_pool_node *node = vma->private;
 
-	return i915_active_ref(&node->active, tl, fence);
+	return i915_active_ref(&node->active, tl->fence_context, fence);
 }
 
 static int
diff --git a/drivers/gpu/drm/i915/gt/intel_timeline.c b/drivers/gpu/drm/i915/gt/intel_timeline.c
index 4546284fede1..e4a5326633b8 100644
--- a/drivers/gpu/drm/i915/gt/intel_timeline.c
+++ b/drivers/gpu/drm/i915/gt/intel_timeline.c
@@ -479,7 +479,9 @@ __intel_timeline_get_seqno(struct intel_timeline *tl,
 	 * free it after the current request is retired, which ensures that
 	 * all writes into the cacheline from previous requests are complete.
 	 */
-	err = i915_active_ref(&tl->hwsp_cacheline->active, tl, &rq->fence);
+	err = i915_active_ref(&tl->hwsp_cacheline->active,
+			      tl->fence_context,
+			      &rq->fence);
 	if (err)
 		goto err_cacheline;
 
diff --git a/drivers/gpu/drm/i915/i915_active.c b/drivers/gpu/drm/i915/i915_active.c
index d960d0be5bd2..3f595446fd44 100644
--- a/drivers/gpu/drm/i915/i915_active.c
+++ b/drivers/gpu/drm/i915/i915_active.c
@@ -217,11 +217,10 @@ excl_retire(struct dma_fence *fence, struct dma_fence_cb *cb)
 }
 
 static struct i915_active_fence *
-active_instance(struct i915_active *ref, struct intel_timeline *tl)
+active_instance(struct i915_active *ref, u64 idx)
 {
 	struct active_node *node, *prealloc;
 	struct rb_node **p, *parent;
-	u64 idx = tl->fence_context;
 
 	/*
 	 * We track the most recently used timeline to skip a rbtree search
@@ -353,21 +352,17 @@ __active_del_barrier(struct i915_active *ref, struct active_node *node)
 	return ____active_del_barrier(ref, node, barrier_to_engine(node));
 }
 
-int i915_active_ref(struct i915_active *ref,
-		    struct intel_timeline *tl,
-		    struct dma_fence *fence)
+int i915_active_ref(struct i915_active *ref, u64 idx, struct dma_fence *fence)
 {
 	struct i915_active_fence *active;
 	int err;
 
-	lockdep_assert_held(&tl->mutex);
-
 	/* Prevent reaping in case we malloc/wait while building the tree */
 	err = i915_active_acquire(ref);
 	if (err)
 		return err;
 
-	active = active_instance(ref, tl);
+	active = active_instance(ref, idx);
 	if (!active) {
 		err = -ENOMEM;
 		goto out;
@@ -384,32 +379,104 @@ int i915_active_ref(struct i915_active *ref,
 		atomic_dec(&ref->count);
 	}
 	if (!__i915_active_fence_set(active, fence))
-		atomic_inc(&ref->count);
+		__i915_active_acquire(ref);
 
 out:
 	i915_active_release(ref);
 	return err;
 }
 
-struct dma_fence *
-i915_active_set_exclusive(struct i915_active *ref, struct dma_fence *f)
+static struct dma_fence *
+__i915_active_set_fence(struct i915_active *ref,
+			struct i915_active_fence *active,
+			struct dma_fence *fence)
 {
 	struct dma_fence *prev;
 
 	/* We expect the caller to manage the exclusive timeline ordering */
 	GEM_BUG_ON(i915_active_is_idle(ref));
 
+	if (is_barrier(active)) { /* proto-node used by our idle barrier */
+		/*
+		 * This request is on the kernel_context timeline, and so
+		 * we can use it to substitute for the pending idle-barrer
+		 * request that we want to emit on the kernel_context.
+		 */
+		__active_del_barrier(ref, node_from_active(active));
+		RCU_INIT_POINTER(active->fence, NULL);
+		atomic_dec(&ref->count);
+	}
+
 	rcu_read_lock();
-	prev = __i915_active_fence_set(&ref->excl, f);
+	prev = __i915_active_fence_set(active, fence);
 	if (prev)
 		prev = dma_fence_get_rcu(prev);
 	else
-		atomic_inc(&ref->count);
+		__i915_active_acquire(ref);
 	rcu_read_unlock();
 
 	return prev;
 }
 
+static struct i915_active_fence *
+__active_lookup(struct i915_active *ref, u64 idx)
+{
+	struct active_node *node;
+	struct rb_node *p;
+
+	/* Like active_instance() but with no malloc */
+
+	node = READ_ONCE(ref->cache);
+	if (node && node->timeline == idx)
+		return &node->base;
+
+	spin_lock_irq(&ref->tree_lock);
+	GEM_BUG_ON(i915_active_is_idle(ref));
+
+	p = ref->tree.rb_node;
+	while (p) {
+		node = rb_entry(p, struct active_node, node);
+		if (node->timeline == idx) {
+			ref->cache = node;
+			spin_unlock_irq(&ref->tree_lock);
+			return &node->base;
+		}
+
+		if (node->timeline < idx)
+			p = p->rb_right;
+		else
+			p = p->rb_left;
+	}
+
+	spin_unlock_irq(&ref->tree_lock);
+
+	return NULL;
+}
+
+struct dma_fence *
+__i915_active_ref(struct i915_active *ref, u64 idx, struct dma_fence *fence)
+{
+	struct dma_fence *prev = ERR_PTR(-ENOENT);
+	struct i915_active_fence *active;
+
+	if (!i915_active_acquire_if_busy(ref))
+		return ERR_PTR(-EINVAL);
+
+	active = __active_lookup(ref, idx);
+	if (active)
+		prev = __i915_active_set_fence(ref, active, fence);
+
+	i915_active_release(ref);
+	return prev;
+}
+
+struct dma_fence *
+i915_active_set_exclusive(struct i915_active *ref, struct dma_fence *f)
+{
+	/* We expect the caller to manage the exclusive timeline ordering */
+	return __i915_active_set_fence(ref, &ref->excl, f);
+}
+
 bool i915_active_acquire_if_busy(struct i915_active *ref)
 {
 	debug_active_assert(ref);
@@ -443,6 +510,24 @@ int i915_active_acquire(struct i915_active *ref)
 	return err;
 }
 
+int i915_active_acquire_for_context(struct i915_active *ref, u64 idx)
+{
+	struct i915_active_fence *active;
+	int err;
+
+	err = i915_active_acquire(ref);
+	if (err)
+		return err;
+
+	active = active_instance(ref, idx);
+	if (!active) {
+		i915_active_release(ref);
+		return -ENOMEM;
+	}
+
+	return 0; /* return with active ref */
+}
+
 void i915_active_release(struct i915_active *ref)
 {
 	debug_active_assert(ref);
@@ -804,7 +889,7 @@ int i915_active_acquire_preallocate_barrier(struct i915_active *ref,
 			 */
 			RCU_INIT_POINTER(node->base.fence, ERR_PTR(-EAGAIN));
 			node->base.cb.node.prev = (void *)engine;
-			atomic_inc(&ref->count);
+			__i915_active_acquire(ref);
 		}
 		GEM_BUG_ON(rcu_access_pointer(node->base.fence) != ERR_PTR(-EAGAIN));
 
diff --git a/drivers/gpu/drm/i915/i915_active.h b/drivers/gpu/drm/i915/i915_active.h
index cf4058150966..2e0bcb3289ec 100644
--- a/drivers/gpu/drm/i915/i915_active.h
+++ b/drivers/gpu/drm/i915/i915_active.h
@@ -163,14 +163,18 @@ void __i915_active_init(struct i915_active *ref,
 	__i915_active_init(ref, active, retire, &__mkey, &__wkey);	\
 } while (0)
 
-int i915_active_ref(struct i915_active *ref,
-		    struct intel_timeline *tl,
-		    struct dma_fence *fence);
+struct dma_fence *
+__i915_active_ref(struct i915_active *ref, u64 idx, struct dma_fence *fence);
+int i915_active_ref(struct i915_active *ref, u64 idx, struct dma_fence *fence);
 
 static inline int
 i915_active_add_request(struct i915_active *ref, struct i915_request *rq)
 {
-	return i915_active_ref(ref, i915_request_timeline(rq), &rq->fence);
+	struct intel_timeline *tl = i915_request_timeline(rq);
+
+	lockdep_assert_held(&tl->mutex);
+
+	return i915_active_ref(ref, tl->fence_context, &rq->fence);
 }
 
 struct dma_fence *
@@ -198,7 +202,9 @@ int i915_request_await_active(struct i915_request *rq,
 #define I915_ACTIVE_AWAIT_BARRIER BIT(2)
 
 int i915_active_acquire(struct i915_active *ref);
+int i915_active_acquire_for_context(struct i915_active *ref, u64 idx);
 bool i915_active_acquire_if_busy(struct i915_active *ref);
+
 void i915_active_release(struct i915_active *ref);
 
 static inline void __i915_active_acquire(struct i915_active *ref)
-- 
2.20.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [Intel-gfx] [PATCH 11/20] drm/i915/gem: Separate the ww_mutex walker into its own list
  2020-07-06  6:19 [Intel-gfx] s/obj->mm.lock// Chris Wilson
                   ` (9 preceding siblings ...)
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 10/20] drm/i915: Export a preallocate variant of i915_active_acquire() Chris Wilson
@ 2020-07-06  6:19 ` Chris Wilson
  2020-07-13 14:53   ` Tvrtko Ursulin
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 12/20] drm/i915/gem: Asynchronous GTT unbinding Chris Wilson
                   ` (13 subsequent siblings)
  24 siblings, 1 reply; 65+ messages in thread
From: Chris Wilson @ 2020-07-06  6:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: Chris Wilson

In preparation for making eb_vma bigger and heavy to run inn parallel,
we need to stop apply an in-place swap() to reorder around ww_mutex
deadlocks. Keep the array intact and reorder the locks using a dedicated
list.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 .../gpu/drm/i915/gem/i915_gem_execbuffer.c    | 83 ++++++++++++-------
 1 file changed, 54 insertions(+), 29 deletions(-)

diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
index 6d20be29ff3c..4d8ac89c56fc 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
@@ -37,6 +37,7 @@ struct eb_vma {
 	struct list_head bind_link;
 	struct list_head unbound_link;
 	struct list_head reloc_link;
+	struct list_head submit_link;
 
 	struct hlist_node node;
 	u32 handle;
@@ -248,6 +249,8 @@ struct i915_execbuffer {
 	/** list of vma that have execobj.relocation_count */
 	struct list_head relocs;
 
+	struct list_head submit_list;
+
 	/**
 	 * Track the most recently used object for relocations, as we
 	 * frequently have to perform multiple relocations within the same
@@ -341,6 +344,42 @@ static void eb_vma_array_put(struct eb_vma_array *arr)
 	kref_put(&arr->kref, eb_vma_array_destroy);
 }
 
+static int
+eb_lock_vma(struct i915_execbuffer *eb, struct ww_acquire_ctx *acquire)
+{
+	struct eb_vma *ev;
+	int err = 0;
+
+	list_for_each_entry(ev, &eb->submit_list, submit_link) {
+		struct i915_vma *vma = ev->vma;
+
+		err = ww_mutex_lock_interruptible(&vma->resv->lock, acquire);
+		if (err == -EDEADLK) {
+			struct eb_vma *unlock = ev, *en;
+
+			list_for_each_entry_safe_continue_reverse(unlock, en,
+								  &eb->submit_list,
+								  submit_link) {
+				ww_mutex_unlock(&unlock->vma->resv->lock);
+				list_move_tail(&unlock->submit_link, &eb->submit_list);
+			}
+
+			GEM_BUG_ON(!list_is_first(&ev->submit_link, &eb->submit_list));
+			err = ww_mutex_lock_slow_interruptible(&vma->resv->lock,
+							       acquire);
+		}
+		if (err) {
+			list_for_each_entry_continue_reverse(ev,
+							     &eb->submit_list,
+							     submit_link)
+				ww_mutex_unlock(&ev->vma->resv->lock);
+			break;
+		}
+	}
+
+	return err;
+}
+
 static int eb_create(struct i915_execbuffer *eb)
 {
 	/* Allocate an extra slot for use by the command parser + sentinel */
@@ -393,6 +432,10 @@ static int eb_create(struct i915_execbuffer *eb)
 		eb->lut_size = -eb->buffer_count;
 	}
 
+	INIT_LIST_HEAD(&eb->bind_list);
+	INIT_LIST_HEAD(&eb->submit_list);
+	INIT_LIST_HEAD(&eb->relocs);
+
 	return 0;
 }
 
@@ -574,6 +617,7 @@ eb_add_vma(struct i915_execbuffer *eb,
 	}
 
 	list_add_tail(&ev->bind_link, &eb->bind_list);
+	list_add_tail(&ev->submit_link, &eb->submit_list);
 
 	if (entry->relocation_count)
 		list_add_tail(&ev->reloc_link, &eb->relocs);
@@ -910,9 +954,6 @@ static int eb_lookup_vmas(struct i915_execbuffer *eb)
 	unsigned int i;
 	int err = 0;
 
-	INIT_LIST_HEAD(&eb->bind_list);
-	INIT_LIST_HEAD(&eb->relocs);
-
 	for (i = 0; i < eb->buffer_count; i++) {
 		struct i915_vma *vma;
 
@@ -1583,38 +1624,19 @@ static int eb_relocate(struct i915_execbuffer *eb)
 
 static int eb_move_to_gpu(struct i915_execbuffer *eb)
 {
-	const unsigned int count = eb->buffer_count;
 	struct ww_acquire_ctx acquire;
-	unsigned int i;
+	struct eb_vma *ev;
 	int err = 0;
 
 	ww_acquire_init(&acquire, &reservation_ww_class);
 
-	for (i = 0; i < count; i++) {
-		struct eb_vma *ev = &eb->vma[i];
-		struct i915_vma *vma = ev->vma;
-
-		err = ww_mutex_lock_interruptible(&vma->resv->lock, &acquire);
-		if (err == -EDEADLK) {
-			GEM_BUG_ON(i == 0);
-			do {
-				int j = i - 1;
-
-				ww_mutex_unlock(&eb->vma[j].vma->resv->lock);
-
-				swap(eb->vma[i],  eb->vma[j]);
-			} while (--i);
+	err = eb_lock_vma(eb, &acquire);
+	if (err)
+		goto err_fini;
 
-			err = ww_mutex_lock_slow_interruptible(&vma->resv->lock,
-							       &acquire);
-		}
-		if (err)
-			break;
-	}
 	ww_acquire_done(&acquire);
 
-	while (i--) {
-		struct eb_vma *ev = &eb->vma[i];
+	list_for_each_entry(ev, &eb->submit_list, submit_link) {
 		struct i915_vma *vma = ev->vma;
 		unsigned int flags = ev->flags;
 		struct drm_i915_gem_object *obj = vma->obj;
@@ -1671,6 +1693,8 @@ static int eb_move_to_gpu(struct i915_execbuffer *eb)
 	intel_gt_chipset_flush(eb->engine->gt);
 	return 0;
 
+err_fini:
+	ww_acquire_fini(&acquire);
 err_skip:
 	i915_request_set_error_once(eb->request, err);
 	return err;
@@ -1952,9 +1976,10 @@ static int eb_parse(struct i915_execbuffer *eb)
 	if (err)
 		goto err_trampoline;
 
-	eb->vma[eb->buffer_count].vma = i915_vma_get(shadow);
-	eb->vma[eb->buffer_count].flags = __EXEC_OBJECT_HAS_PIN;
 	eb->batch = &eb->vma[eb->buffer_count++];
+	eb->batch->vma = i915_vma_get(shadow);
+	eb->batch->flags = __EXEC_OBJECT_HAS_PIN;
+	list_add_tail(&eb->batch->submit_link, &eb->submit_list);
 	eb->vma[eb->buffer_count].vma = NULL;
 
 	eb->trampoline = trampoline;
-- 
2.20.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [Intel-gfx] [PATCH 12/20] drm/i915/gem: Asynchronous GTT unbinding
  2020-07-06  6:19 [Intel-gfx] s/obj->mm.lock// Chris Wilson
                   ` (10 preceding siblings ...)
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 11/20] drm/i915/gem: Separate the ww_mutex walker into its own list Chris Wilson
@ 2020-07-06  6:19 ` Chris Wilson
  2020-07-14  9:02   ` Tvrtko Ursulin
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 13/20] drm/i915/gem: Bind the fence async for execbuf Chris Wilson
                   ` (12 subsequent siblings)
  24 siblings, 1 reply; 65+ messages in thread
From: Chris Wilson @ 2020-07-06  6:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: Matthew Auld, Chris Wilson

It is reasonably common for userspace (even modern drivers like iris) to
reuse an active address for a new buffer. This would cause the
application to stall under its mutex (originally struct_mutex) until the
old batches were idle and it could synchronously remove the stale PTE.
However, we can queue up a job that waits on the signal for the old
nodes to complete and upon those signals, remove the old nodes replacing
them with the new ones for the batch. This is still CPU driven, but in
theory we can do the GTT patching from the GPU. The job itself has a
completion signal allowing the execbuf to wait upon the rebinding, and
also other observers to coordinate with the common VM activity.

Letting userspace queue up more work, lets it do more stuff without
blocking other clients. In turn, we take care not to let it too much
concurrent work, creating a small number of queues for each context to
limit the number of concurrent tasks.

The implementation relies on only scheduling one unbind operation per
vma as we use the unbound vma->node location to track the stale PTE.

Closes: https://gitlab.freedesktop.org/drm/intel/issues/1402
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Matthew Auld <matthew.auld@intel.com>
Cc: Andi Shyti <andi.shyti@intel.com>
---
 .../gpu/drm/i915/gem/i915_gem_execbuffer.c    | 917 ++++++++++++++++--
 drivers/gpu/drm/i915/gt/gen6_ppgtt.c          |   1 +
 drivers/gpu/drm/i915/gt/intel_gtt.c           |   4 +
 drivers/gpu/drm/i915/gt/intel_gtt.h           |   2 +
 drivers/gpu/drm/i915/i915_gem.c               |   7 +
 drivers/gpu/drm/i915/i915_gem_gtt.c           |   5 +
 drivers/gpu/drm/i915/i915_vma.c               |  71 +-
 drivers/gpu/drm/i915/i915_vma.h               |   4 +
 8 files changed, 883 insertions(+), 128 deletions(-)

diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
index 4d8ac89c56fc..6a406e8798ef 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
@@ -18,6 +18,7 @@
 #include "gt/intel_gt.h"
 #include "gt/intel_gt_buffer_pool.h"
 #include "gt/intel_gt_pm.h"
+#include "gt/intel_gt_requests.h"
 #include "gt/intel_ring.h"
 
 #include "i915_drv.h"
@@ -43,6 +44,12 @@ struct eb_vma {
 	u32 handle;
 };
 
+struct eb_bind_vma {
+	struct eb_vma *ev;
+	struct drm_mm_node hole;
+	unsigned int bind_flags;
+};
+
 struct eb_vma_array {
 	struct kref kref;
 	struct eb_vma vma[];
@@ -66,11 +73,12 @@ struct eb_vma_array {
 	 I915_EXEC_RESOURCE_STREAMER)
 
 /* Catch emission of unexpected errors for CI! */
+#define __EINVAL__ 22
 #if IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM)
 #undef EINVAL
 #define EINVAL ({ \
 	DRM_DEBUG_DRIVER("EINVAL at %s:%d\n", __func__, __LINE__); \
-	22; \
+	__EINVAL__; \
 })
 #endif
 
@@ -311,6 +319,12 @@ static struct eb_vma_array *eb_vma_array_create(unsigned int count)
 	return arr;
 }
 
+static struct eb_vma_array *eb_vma_array_get(struct eb_vma_array *arr)
+{
+	kref_get(&arr->kref);
+	return arr;
+}
+
 static inline void eb_unreserve_vma(struct eb_vma *ev)
 {
 	struct i915_vma *vma = ev->vma;
@@ -444,7 +458,10 @@ eb_vma_misplaced(const struct drm_i915_gem_exec_object2 *entry,
 		 const struct i915_vma *vma,
 		 unsigned int flags)
 {
-	if (vma->node.size < entry->pad_to_size)
+	if (test_bit(I915_VMA_ERROR_BIT, __i915_vma_flags(vma)))
+		return true;
+
+	if (vma->node.size < max(vma->size, entry->pad_to_size))
 		return true;
 
 	if (entry->alignment && !IS_ALIGNED(vma->node.start, entry->alignment))
@@ -469,32 +486,6 @@ eb_vma_misplaced(const struct drm_i915_gem_exec_object2 *entry,
 	return false;
 }
 
-static u64 eb_pin_flags(const struct drm_i915_gem_exec_object2 *entry,
-			unsigned int exec_flags)
-{
-	u64 pin_flags = 0;
-
-	if (exec_flags & EXEC_OBJECT_NEEDS_GTT)
-		pin_flags |= PIN_GLOBAL;
-
-	/*
-	 * Wa32bitGeneralStateOffset & Wa32bitInstructionBaseOffset,
-	 * limit address to the first 4GBs for unflagged objects.
-	 */
-	if (!(exec_flags & EXEC_OBJECT_SUPPORTS_48B_ADDRESS))
-		pin_flags |= PIN_ZONE_4G;
-
-	if (exec_flags & __EXEC_OBJECT_NEEDS_MAP)
-		pin_flags |= PIN_MAPPABLE;
-
-	if (exec_flags & EXEC_OBJECT_PINNED)
-		pin_flags |= entry->offset | PIN_OFFSET_FIXED;
-	else if (exec_flags & __EXEC_OBJECT_NEEDS_BIAS)
-		pin_flags |= BATCH_OFFSET_BIAS | PIN_OFFSET_BIAS;
-
-	return pin_flags;
-}
-
 static bool eb_pin_vma_fence_inplace(struct eb_vma *ev)
 {
 	struct i915_vma *vma = ev->vma;
@@ -522,6 +513,10 @@ eb_pin_vma_inplace(struct i915_execbuffer *eb,
 	struct i915_vma *vma = ev->vma;
 	unsigned int pin_flags;
 
+	/* Concurrent async binds in progress, get in the queue */
+	if (!i915_active_is_idle(&vma->vm->binding))
+		return false;
+
 	if (eb_vma_misplaced(entry, vma, ev->flags))
 		return false;
 
@@ -642,45 +637,463 @@ eb_add_vma(struct i915_execbuffer *eb,
 	}
 }
 
-static int eb_reserve_vma(const struct i915_execbuffer *eb,
-			  struct eb_vma *ev,
-			  u64 pin_flags)
+struct eb_vm_work {
+	struct dma_fence_work base;
+	struct eb_vma_array *array;
+	struct eb_bind_vma *bind;
+	struct i915_address_space *vm;
+	struct i915_vm_pt_stash stash;
+	struct list_head evict_list;
+	u64 *p_flags;
+	u64 id;
+	unsigned long count;
+};
+
+static inline u64 node_end(const struct drm_mm_node *node)
+{
+	return node->start + node->size;
+}
+
+static int set_bind_fence(struct i915_vma *vma, struct eb_vm_work *work)
+{
+	struct dma_fence *prev;
+	int err = 0;
+
+	lockdep_assert_held(&vma->vm->mutex);
+	prev = i915_active_set_exclusive(&vma->active, &work->base.dma);
+	if (unlikely(prev)) {
+		err = i915_sw_fence_await_dma_fence(&work->base.chain, prev, 0,
+						    GFP_NOWAIT | __GFP_NOWARN);
+		dma_fence_put(prev);
+	}
+
+	return err < 0 ? err : 0;
+}
+
+static int await_evict(struct eb_vm_work *work, struct i915_vma *vma)
 {
-	struct drm_i915_gem_exec_object2 *entry = ev->exec;
-	struct i915_vma *vma = ev->vma;
 	int err;
 
-	if (drm_mm_node_allocated(&vma->node) &&
-	    eb_vma_misplaced(entry, vma, ev->flags)) {
-		err = i915_vma_unbind(vma);
+	GEM_BUG_ON(rcu_access_pointer(vma->active.excl.fence) == &work->base.dma);
+
+	/* Wait for all other previous activity */
+	err = i915_sw_fence_await_active(&work->base.chain,
+					 &vma->active,
+					 I915_ACTIVE_AWAIT_ACTIVE);
+	/* Then insert along the exclusive vm->mutex timeline */
+	if (err == 0)
+		err = set_bind_fence(vma, work);
+
+	return err;
+}
+
+static int
+evict_for_node(struct eb_vm_work *work,
+	       struct eb_bind_vma *const target,
+	       unsigned int flags)
+{
+	struct i915_vma *target_vma = target->ev->vma;
+	struct i915_address_space *vm = target_vma->vm;
+	const unsigned long color = target_vma->node.color;
+	const u64 start = target_vma->node.start;
+	const u64 end = start + target_vma->node.size;
+	u64 hole_start = start, hole_end = end;
+	struct i915_vma *vma, *next;
+	struct drm_mm_node *node;
+	LIST_HEAD(evict_list);
+	LIST_HEAD(steal_list);
+	int err = 0;
+
+	lockdep_assert_held(&vm->mutex);
+	GEM_BUG_ON(drm_mm_node_allocated(&target_vma->node));
+	GEM_BUG_ON(!IS_ALIGNED(start, I915_GTT_PAGE_SIZE));
+	GEM_BUG_ON(!IS_ALIGNED(end, I915_GTT_PAGE_SIZE));
+
+	if (i915_vm_has_cache_coloring(vm)) {
+		/* Expand search to cover neighbouring guard pages (or lack!) */
+		if (hole_start)
+			hole_start -= I915_GTT_PAGE_SIZE;
+
+		/* Always look at the page afterwards to avoid the end-of-GTT */
+		hole_end += I915_GTT_PAGE_SIZE;
+	}
+	GEM_BUG_ON(hole_start >= hole_end);
+
+	drm_mm_for_each_node_in_range(node, &vm->mm, hole_start, hole_end) {
+		GEM_BUG_ON(node == &target_vma->node);
+		err = -ENOSPC;
+
+		/* If we find any non-objects (!vma), we cannot evict them */
+		if (node->color == I915_COLOR_UNEVICTABLE)
+			goto err;
+
+		/*
+		 * If we are using coloring to insert guard pages between
+		 * different cache domains within the address space, we have
+		 * to check whether the objects on either side of our range
+		 * abutt and conflict. If they are in conflict, then we evict
+		 * those as well to make room for our guard pages.
+		 */
+		if (i915_vm_has_cache_coloring(vm)) {
+			if (node_end(node) == start && node->color == color)
+				continue;
+
+			if (node->start == end && node->color == color)
+				continue;
+		}
+
+		GEM_BUG_ON(!drm_mm_node_allocated(node));
+		vma = container_of(node, typeof(*vma), node);
+
+		if (flags & PIN_NOEVICT || i915_vma_is_pinned(vma))
+			goto err;
+
+		/* If this VMA is already being freed, or idle, steal it! */
+		if (!i915_active_acquire_if_busy(&vma->active)) {
+			list_move(&vma->vm_link, &steal_list);
+			continue;
+		}
+
+		if (!(flags & PIN_NONBLOCK))
+			err = await_evict(work, vma);
+		i915_active_release(&vma->active);
 		if (err)
-			return err;
+			goto err;
+
+		GEM_BUG_ON(!i915_vma_is_active(vma));
+		list_move(&vma->vm_link, &evict_list);
 	}
 
-	err = i915_vma_pin(vma,
-			   entry->pad_to_size, entry->alignment,
-			   eb_pin_flags(entry, ev->flags) | pin_flags);
-	if (err)
-		return err;
+	list_for_each_entry_safe(vma, next, &steal_list, vm_link) {
+		GEM_BUG_ON(i915_vma_is_pinned(vma));
+		GEM_BUG_ON(i915_vma_is_active(vma));
+		__i915_vma_evict(vma);
+		drm_mm_remove_node(&vma->node);
+		/* No ref held; vma may now be concurrently freed */
+	}
 
-	if (entry->offset != vma->node.start) {
-		entry->offset = vma->node.start | UPDATE;
-		eb->args->flags |= __EXEC_HAS_RELOC;
+	/* No overlapping nodes to evict, claim the slot for ourselves! */
+	if (list_empty(&evict_list))
+		return drm_mm_reserve_node(&vm->mm, &target_vma->node);
+
+	/*
+	 * Mark this range as reserved.
+	 *
+	 * We have not yet removed the PTEs for the old evicted nodes, so
+	 * must prevent this range from being reused for anything else. The
+	 * PTE will be cleared when the range is idle (during the rebind
+	 * phase in the worker).
+	 */
+	target->hole.color = I915_COLOR_UNEVICTABLE;
+	target->hole.start = start;
+	target->hole.size = end;
+
+	list_for_each_entry(vma, &evict_list, vm_link) {
+		target->hole.start =
+			min(target->hole.start, vma->node.start);
+		target->hole.size =
+			max(target->hole.size, node_end(&vma->node));
+
+		GEM_BUG_ON(!i915_vma_is_active(vma));
+		GEM_BUG_ON(vma->node.mm != &vm->mm);
+		set_bit(I915_VMA_ERROR_BIT, __i915_vma_flags(vma));
+		drm_mm_remove_node(&vma->node);
+		GEM_BUG_ON(i915_vma_is_pinned(vma));
 	}
+	list_splice(&evict_list, &work->evict_list);
 
-	if (unlikely(ev->flags & EXEC_OBJECT_NEEDS_FENCE)) {
-		err = i915_vma_pin_fence(vma);
-		if (unlikely(err)) {
-			i915_vma_unpin(vma);
-			return err;
+	target->hole.size -= target->hole.start;
+
+	return drm_mm_reserve_node(&vm->mm, &target->hole);
+
+err:
+	list_splice(&evict_list, &vm->bound_list);
+	list_splice(&steal_list, &vm->bound_list);
+	return err;
+}
+
+static int
+evict_in_range(struct eb_vm_work *work,
+	       struct eb_bind_vma * const target,
+	       u64 start, u64 end, u64 align)
+{
+	struct i915_vma *target_vma = target->ev->vma;
+	struct i915_address_space *vm = target_vma->vm;
+	struct i915_vma *active = NULL;
+	struct i915_vma *vma, *next;
+	struct drm_mm_scan scan;
+	LIST_HEAD(evict_list);
+	bool found = false;
+
+	lockdep_assert_held(&vm->mutex);
+
+	drm_mm_scan_init_with_range(&scan, &vm->mm,
+				    target_vma->node.size,
+				    align,
+				    target_vma->node.color,
+				    start, end,
+				    DRM_MM_INSERT_BEST);
+
+	list_for_each_entry_safe(vma, next, &vm->bound_list, vm_link) {
+		if (i915_vma_is_pinned(vma))
+			continue;
+
+		if (vma == active)
+			active = ERR_PTR(-EAGAIN);
+
+		/* Prefer to reuse idle nodes; push all active vma to the end */
+		if (active != ERR_PTR(-EAGAIN) && i915_vma_is_active(vma)) {
+			if (!active)
+				active = vma;
+
+			list_move_tail(&vma->vm_link, &vm->bound_list);
+			continue;
 		}
 
+		list_move(&vma->vm_link, &evict_list);
+		if (drm_mm_scan_add_block(&scan, &vma->node)) {
+			target_vma->node.start =
+				round_up(scan.hit_start, align);
+			found = true;
+			break;
+		}
+	}
+
+	list_for_each_entry(vma, &evict_list, vm_link)
+		drm_mm_scan_remove_block(&scan, &vma->node);
+	list_splice(&evict_list, &vm->bound_list);
+	if (!found)
+		return -ENOSPC;
+
+	return evict_for_node(work, target, 0);
+}
+
+static u64 random_offset(u64 start, u64 end, u64 len, u64 align)
+{
+	u64 range, addr;
+
+	GEM_BUG_ON(range_overflows(start, len, end));
+	GEM_BUG_ON(round_up(start, align) > round_down(end - len, align));
+
+	range = round_down(end - len, align) - round_up(start, align);
+	if (range) {
+		if (sizeof(unsigned long) == sizeof(u64)) {
+			addr = get_random_long();
+		} else {
+			addr = get_random_int();
+			if (range > U32_MAX) {
+				addr <<= 32;
+				addr |= get_random_int();
+			}
+		}
+		div64_u64_rem(addr, range, &addr);
+		start += addr;
+	}
+
+	return round_up(start, align);
+}
+
+static u64 align0(u64 align)
+{
+	return align <= I915_GTT_MIN_ALIGNMENT ? 0 : align;
+}
+
+static struct drm_mm_node *__best_hole(struct drm_mm *mm, u64 size)
+{
+	struct rb_node *rb = mm->holes_size.rb_root.rb_node;
+	struct drm_mm_node *best = NULL;
+
+	while (rb) {
+		struct drm_mm_node *node =
+			rb_entry(rb, struct drm_mm_node, rb_hole_size);
+
+		if (size <= node->hole_size) {
+			best = node;
+			rb = rb->rb_right;
+		} else {
+			rb = rb->rb_left;
+		}
+	}
+
+	return best;
+}
+
+static int best_hole(struct drm_mm *mm, struct drm_mm_node *node,
+		     u64 start, u64 end, u64 align)
+{
+	struct drm_mm_node *hole;
+	u64 size = node->size;
+
+	do {
+		hole = __best_hole(mm, size);
+		if (!hole)
+			return -ENOSPC;
+
+		node->start = round_up(max(start, drm_mm_hole_node_start(hole)),
+				       align);
+		if (min(drm_mm_hole_node_end(hole), end) >=
+		    node->start + node->size)
+			return drm_mm_reserve_node(mm, node);
+
+		/*
+		 * Too expensive to search for every single hole every time,
+		 * so just look for the next bigger hole, introducing enough
+		 * space for alignments. Finding the smallest hole with ideal
+		 * alignment scales very poorly, so we choose to waste space
+		 * if an alignment is forced. On the other hand, simply
+		 * randomly selecting an offset in 48b space will cause us
+		 * to use the majority of that space and exhaust all memory
+		 * in storing the page directories. Compromise is required.
+		 */
+		size = hole->hole_size + align;
+	} while (1);
+}
+
+static int eb_reserve_vma(struct eb_vm_work *work, struct eb_bind_vma *bind)
+{
+	struct drm_i915_gem_exec_object2 *entry = bind->ev->exec;
+	const unsigned int exec_flags = bind->ev->flags;
+	struct i915_vma *vma = bind->ev->vma;
+	struct i915_address_space *vm = vma->vm;
+	u64 start = 0, end = vm->total;
+	u64 align = entry->alignment ?: I915_GTT_MIN_ALIGNMENT;
+	unsigned int bind_flags;
+	int err;
+
+	lockdep_assert_held(&vm->mutex);
+
+	bind_flags = PIN_USER;
+	if (exec_flags & EXEC_OBJECT_NEEDS_GTT)
+		bind_flags |= PIN_GLOBAL;
+
+	if (drm_mm_node_allocated(&vma->node))
+		goto pin;
+
+	GEM_BUG_ON(i915_vma_is_pinned(vma));
+	GEM_BUG_ON(i915_vma_is_bound(vma, I915_VMA_BIND_MASK));
+	GEM_BUG_ON(i915_active_fence_isset(&vma->active.excl));
+	GEM_BUG_ON(!vma->size);
+
+	/* Reuse old address (if it doesn't conflict with new requirements) */
+	if (eb_vma_misplaced(entry, vma, exec_flags)) {
+		vma->node.start = entry->offset & PIN_OFFSET_MASK;
+		vma->node.size = max(entry->pad_to_size, vma->size);
+		vma->node.color = 0;
+		if (i915_vm_has_cache_coloring(vm))
+			vma->node.color = vma->obj->cache_level;
+	}
+
+	/*
+	 * Wa32bitGeneralStateOffset & Wa32bitInstructionBaseOffset,
+	 * limit address to the first 4GBs for unflagged objects.
+	 */
+	if (!(exec_flags & EXEC_OBJECT_SUPPORTS_48B_ADDRESS))
+		end = min_t(u64, end, (1ULL << 32) - I915_GTT_PAGE_SIZE);
+
+	align = max(align, vma->display_alignment);
+	if (exec_flags & __EXEC_OBJECT_NEEDS_MAP) {
+		vma->node.size = max_t(u64, vma->node.size, vma->fence_size);
+		end = min_t(u64, end, i915_vm_to_ggtt(vm)->mappable_end);
+		align = max_t(u64, align, vma->fence_alignment);
+	}
+
+	if (exec_flags & __EXEC_OBJECT_NEEDS_BIAS)
+		start = BATCH_OFFSET_BIAS;
+
+	GEM_BUG_ON(!vma->node.size);
+	if (vma->node.size > end - start)
+		return -E2BIG;
+
+	/* Try the user's preferred location first (mandatory if soft-pinned) */
+	err = -__EINVAL__;
+	if (vma->node.start >= start &&
+	    IS_ALIGNED(vma->node.start, align) &&
+	    !range_overflows(vma->node.start, vma->node.size, end)) {
+		unsigned int pin_flags;
+
+		/*
+		 * Prefer to relocate and spread objects around.
+		 *
+		 * If we relocate and continue to use the new location in
+		 * future batches, we only pay the relocation cost once.
+		 *
+		 * If we instead keep reusing the same address for different
+		 * objects, each batch must remove/insert objects into the GTT,
+		 * which is more expensive than performing a relocation.
+		 */
+		pin_flags = 0;
+		if (!(exec_flags & EXEC_OBJECT_PINNED))
+			pin_flags = PIN_NOEVICT;
+
+		err = evict_for_node(work, bind, pin_flags);
+		if (err == 0)
+			goto pin;
+	}
+	if (exec_flags & EXEC_OBJECT_PINNED)
+		return err;
+
+	/* Try the first available free space */
+	if (!best_hole(&vm->mm, &vma->node, start, end, align))
+		goto pin;
+
+	/* Pick a random slot and see if it's available [O(N) worst case] */
+	vma->node.start = random_offset(start, end, vma->node.size, align);
+	if (evict_for_node(work, bind, PIN_NONBLOCK) == 0)
+		goto pin;
+
+	/* Otherwise search all free space [degrades to O(N^2)] */
+	if (drm_mm_insert_node_in_range(&vm->mm, &vma->node,
+					vma->node.size,
+					align0(align),
+					vma->node.color,
+					start, end,
+					DRM_MM_INSERT_BEST) == 0)
+		goto pin;
+
+	/* Pretty busy! Loop over "LRU" and evict oldest in our search range */
+	err = evict_in_range(work, bind, start, end, align);
+	if (unlikely(err))
+		return err;
+
+pin:
+	if (unlikely(exec_flags & EXEC_OBJECT_NEEDS_FENCE)) {
+		err = __i915_vma_pin_fence(vma); /* XXX no waiting */
+		if (unlikely(err))
+			return err;
+
 		if (vma->fence)
-			ev->flags |= __EXEC_OBJECT_HAS_FENCE;
+			bind->ev->flags |= __EXEC_OBJECT_HAS_FENCE;
 	}
 
-	ev->flags |= __EXEC_OBJECT_HAS_PIN;
-	GEM_BUG_ON(eb_vma_misplaced(entry, vma, ev->flags));
+	bind_flags &= ~atomic_read(&vma->flags);
+	if (bind_flags) {
+		err = set_bind_fence(vma, work);
+		if (unlikely(err))
+			return err;
+
+		atomic_add(I915_VMA_PAGES_ACTIVE, &vma->pages_count);
+		atomic_or(bind_flags, &vma->flags);
+
+		if (i915_vma_is_ggtt(vma))
+			__i915_vma_set_map_and_fenceable(vma);
+
+		GEM_BUG_ON(!i915_vma_is_active(vma));
+		list_move_tail(&vma->vm_link, &vm->bound_list);
+		bind->bind_flags = bind_flags;
+	}
+	__i915_vma_pin(vma); /* and release */
+
+	GEM_BUG_ON(!bind_flags && !drm_mm_node_allocated(&vma->node));
+	GEM_BUG_ON(!(drm_mm_node_allocated(&vma->node) ^
+		     drm_mm_node_allocated(&bind->hole)));
+
+	if (entry->offset != vma->node.start) {
+		entry->offset = vma->node.start | UPDATE;
+		*work->p_flags |= __EXEC_HAS_RELOC;
+	}
+
+	bind->ev->flags |= __EXEC_OBJECT_HAS_PIN;
+	GEM_BUG_ON(eb_vma_misplaced(entry, vma, bind->ev->flags));
 
 	return 0;
 }
@@ -714,13 +1127,244 @@ static int wait_for_timeline(struct intel_timeline *tl)
 	} while (1);
 }
 
+static void __eb_bind_vma(struct eb_vm_work *work)
+{
+	struct i915_address_space *vm = work->vm;
+	unsigned long n;
+
+	GEM_BUG_ON(!intel_gt_pm_is_awake(vm->gt));
+
+	/*
+	 * We have to wait until the stale nodes are completely idle before
+	 * we can remove their PTE and unbind their pages. Hence, after
+	 * claiming their slot in the drm_mm, we defer their removal to
+	 * after the fences are signaled.
+	 */
+	if (!list_empty(&work->evict_list)) {
+		struct i915_vma *vma, *vn;
+
+		mutex_lock(&vm->mutex);
+		list_for_each_entry_safe(vma, vn, &work->evict_list, vm_link) {
+			GEM_BUG_ON(vma->vm != vm);
+			__i915_vma_evict(vma);
+			GEM_BUG_ON(!i915_vma_is_active(vma));
+		}
+		mutex_unlock(&vm->mutex);
+	}
+
+	/*
+	 * Now we know the nodes we require in drm_mm are idle, we can
+	 * replace the PTE in those ranges with our own.
+	 */
+	for (n = 0; n < work->count; n++) {
+		struct eb_bind_vma *bind = &work->bind[n];
+		struct i915_vma *vma = bind->ev->vma;
+
+		if (!bind->bind_flags)
+			goto put;
+
+		GEM_BUG_ON(vma->vm != vm);
+		GEM_BUG_ON(!i915_vma_is_active(vma));
+
+		vma->ops->bind_vma(vm, &work->stash, vma,
+				   vma->obj->cache_level, bind->bind_flags);
+
+		if (drm_mm_node_allocated(&bind->hole)) {
+			mutex_lock(&vm->mutex);
+			GEM_BUG_ON(bind->hole.mm != &vm->mm);
+			GEM_BUG_ON(bind->hole.color != I915_COLOR_UNEVICTABLE);
+			GEM_BUG_ON(drm_mm_node_allocated(&vma->node));
+
+			drm_mm_remove_node(&bind->hole);
+			drm_mm_reserve_node(&vm->mm, &vma->node);
+
+			GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
+			mutex_unlock(&vm->mutex);
+		}
+		bind->bind_flags = 0;
+
+put:
+		GEM_BUG_ON(drm_mm_node_allocated(&bind->hole));
+		i915_vma_put_pages(vma);
+	}
+	work->count = 0;
+}
+
+static int eb_bind_vma(struct dma_fence_work *base)
+{
+	struct eb_vm_work *work = container_of(base, typeof(*work), base);
+
+	__eb_bind_vma(work);
+	return 0;
+}
+
+static void eb_vma_work_release(struct dma_fence_work *base)
+{
+	struct eb_vm_work *work = container_of(base, typeof(*work), base);
+
+	__eb_bind_vma(work);
+	kvfree(work->bind);
+
+	if (work->id)
+		i915_active_release(&work->vm->binding);
+
+	eb_vma_array_put(work->array);
+
+	i915_vm_free_pt_stash(work->vm, &work->stash);
+	i915_vm_put(work->vm);
+}
+
+static const struct dma_fence_work_ops eb_bind_ops = {
+	.name = "eb_bind",
+	.work = eb_bind_vma,
+	.release = eb_vma_work_release,
+};
+
+static int eb_vm_work_cancel(struct eb_vm_work *work, int err)
+{
+	work->base.dma.error = err;
+	dma_fence_work_commit_imm(&work->base);
+
+	return err;
+}
+
+static struct eb_vm_work *eb_vm_work(struct i915_execbuffer *eb,
+				     unsigned long count)
+{
+	struct eb_vm_work *work;
+
+	work = kmalloc(sizeof(*work), GFP_KERNEL);
+	if (!work)
+		return NULL;
+
+	work->bind = kvmalloc(sizeof(*work->bind) * count, GFP_KERNEL);
+	if (!work->bind) {
+		kfree(work->bind);
+		return NULL;
+	}
+	work->count = count;
+
+	INIT_LIST_HEAD(&work->evict_list);
+
+	dma_fence_work_init(&work->base, &eb_bind_ops);
+	work->array = eb_vma_array_get(eb->array);
+	work->p_flags = &eb->args->flags;
+	work->vm = i915_vm_get(eb->context->vm);
+	memset(&work->stash, 0, sizeof(work->stash));
+
+	/* Preallocate our slot in vm->binding, outside of vm->mutex */
+	work->id = i915_gem_context_async_id(eb->gem_context);
+	if (i915_active_acquire_for_context(&work->vm->binding, work->id)) {
+		work->id = 0;
+		eb_vm_work_cancel(work, -ENOMEM);
+		return NULL;
+	}
+
+	return work;
+}
+
+static int eb_vm_throttle(struct eb_vm_work *work)
+{
+	struct dma_fence *p;
+	int err;
+
+	/* Keep async work queued per context */
+	p = __i915_active_ref(&work->vm->binding, work->id, &work->base.dma);
+	if (IS_ERR_OR_NULL(p))
+		return PTR_ERR_OR_ZERO(p);
+
+	err = i915_sw_fence_await_dma_fence(&work->base.chain, p, 0,
+					    GFP_NOWAIT | __GFP_NOWARN);
+	dma_fence_put(p);
+
+	return err < 0 ? err : 0;
+}
+
+static int eb_prepare_vma(struct eb_vm_work *work,
+			  unsigned long idx,
+			  struct eb_vma *ev)
+{
+	struct eb_bind_vma *bind = &work->bind[idx];
+	struct i915_vma *vma = ev->vma;
+	u64 max_size;
+	int err;
+
+	bind->ev = ev;
+	bind->hole.flags = 0;
+	bind->bind_flags = 0;
+
+	/* Allocate enough page directories to cover worst case */
+	max_size = max(vma->size, ev->exec->pad_to_size);
+	if (ev->flags & __EXEC_OBJECT_NEEDS_MAP)
+		max_size = max_t(u64, max_size, vma->fence_size);
+
+	err = i915_vm_alloc_pt_stash(work->vm, &work->stash, max_size);
+	if (err)
+		return err;
+
+	return i915_vma_get_pages(vma);
+}
+
+static int wait_for_unbinds(struct i915_execbuffer *eb,
+			    struct list_head *unbound,
+			    int pass)
+{
+	struct eb_vma *ev;
+	int err;
+
+	list_for_each_entry(ev, unbound, unbound_link) {
+		struct i915_vma *vma = ev->vma;
+
+		GEM_BUG_ON(ev->flags & __EXEC_OBJECT_HAS_PIN);
+
+		if (drm_mm_node_allocated(&vma->node) &&
+		    eb_vma_misplaced(ev->exec, vma, ev->flags)) {
+			err = i915_vma_unbind(vma);
+			if (err)
+				return err;
+		}
+
+		/* Wait for previous to avoid reusing vma->node */
+		err = i915_vma_wait_for_unbind(vma);
+		if (err)
+			return err;
+	}
+
+	switch (pass) {
+	default:
+		return -ENOSPC;
+
+	case 2:
+		/*
+		 * Too fragmented, retire everything on the timeline and so
+		 * make it all [contexts included] available to evict.
+		 */
+		err = wait_for_timeline(eb->context->timeline);
+		if (err)
+			return err;
+
+		fallthrough;
+	case 1:
+		/* XXX ticket lock */
+		if (i915_active_wait(&eb->context->vm->binding))
+			return -EINTR;
+
+		fallthrough;
+	case 0:
+		return 0;
+	}
+}
+
 static int eb_reserve_vm(struct i915_execbuffer *eb)
 {
-	unsigned int pin_flags = PIN_USER | PIN_NONBLOCK;
+	struct i915_address_space *vm = eb->context->vm;
 	struct list_head last, unbound;
+	unsigned long count;
 	struct eb_vma *ev;
 	unsigned int pass;
+	int err = 0;
 
+	count = 0;
 	INIT_LIST_HEAD(&unbound);
 	list_for_each_entry(ev, &eb->bind_list, bind_link) {
 		struct drm_i915_gem_exec_object2 *entry = ev->exec;
@@ -737,44 +1381,93 @@ static int eb_reserve_vm(struct i915_execbuffer *eb)
 				list_add(&ev->unbound_link, &unbound);
 			else
 				list_add_tail(&ev->unbound_link, &unbound);
+			count++;
 		}
 	}
-
-	if (list_empty(&unbound))
+	if (count == 0)
 		return 0;
 
-	/*
-	 * Attempt to pin all of the buffers into the GTT.
-	 * This is done in 3 phases:
-	 *
-	 * 1a. Unbind all objects that do not match the GTT constraints for
-	 *     the execbuffer (fenceable, mappable, alignment etc).
-	 * 1b. Increment pin count for already bound objects.
-	 * 2.  Bind new objects.
-	 * 3.  Decrement pin count.
-	 *
-	 * This avoid unnecessary unbinding of later objects in order to make
-	 * room for the earlier objects *unless* we need to defragment.
-	 */
-
 	pass = 0;
 	do {
-		int err = 0;
+		struct eb_vm_work *work;
 
-		if (mutex_lock_interruptible(&eb->i915->drm.struct_mutex))
-			return -EINTR;
+		work = eb_vm_work(eb, count);
+		if (!work)
+			return -ENOMEM;
 
+		count = 0;
 		list_for_each_entry(ev, &unbound, unbound_link) {
-			err = eb_reserve_vma(eb, ev, pin_flags);
+			err = eb_prepare_vma(work, count++, ev);
+			if (err) {
+				work->count = count - 1;
+
+				if (eb_vm_work_cancel(work, err) == -EAGAIN)
+					goto retry;
+
+				return err;
+			}
+		}
+
+		err = i915_vm_pin_pt_stash(work->vm, &work->stash);
+		if (err)
+			return eb_vm_work_cancel(work, err);
+
+		/* No allocations allowed beyond this point */
+		if (mutex_lock_interruptible(&vm->mutex))
+			return eb_vm_work_cancel(work, -EINTR);
+
+		err = eb_vm_throttle(work);
+		if (err) {
+			mutex_unlock(&vm->mutex);
+			return eb_vm_work_cancel(work, err);
+		}
+
+		for (count = 0; count < work->count; count++) {
+			struct eb_bind_vma *bind = &work->bind[count];
+			struct i915_vma *vma;
+
+			ev = bind->ev;
+			vma = ev->vma;
+
+			/*
+			 * Check if this node is being evicted or must be.
+			 *
+			 * As we use the single node inside the vma to track
+			 * both the eviction and where to insert the new node,
+			 * we cannot handle migrating the vma inside the worker.
+			 */
+			if (drm_mm_node_allocated(&vma->node)) {
+				if (eb_vma_misplaced(ev->exec, vma, ev->flags)) {
+					err = -ENOSPC;
+					break;
+				}
+			} else {
+				if (i915_vma_is_active(vma)) {
+					err = -ENOSPC;
+					break;
+				}
+			}
+
+			err = i915_active_acquire(&vma->active);
+			if (!err) {
+				err = eb_reserve_vma(work, bind);
+				i915_active_release(&vma->active);
+			}
 			if (err)
 				break;
+
+			GEM_BUG_ON(!i915_vma_is_pinned(vma));
 		}
-		if (!(err == -ENOSPC || err == -EAGAIN)) {
-			mutex_unlock(&eb->i915->drm.struct_mutex);
+
+		mutex_unlock(&vm->mutex);
+
+		dma_fence_work_commit_imm(&work->base);
+		if (err != -ENOSPC)
 			return err;
-		}
 
+retry:
 		/* Resort *all* the objects into priority order */
+		count = 0;
 		INIT_LIST_HEAD(&unbound);
 		INIT_LIST_HEAD(&last);
 		list_for_each_entry(ev, &eb->bind_list, bind_link) {
@@ -785,6 +1478,7 @@ static int eb_reserve_vm(struct i915_execbuffer *eb)
 				continue;
 
 			eb_unreserve_vma(ev);
+			count++;
 
 			if (flags & EXEC_OBJECT_PINNED)
 				/* Pinned must have their slot */
@@ -799,34 +1493,21 @@ static int eb_reserve_vm(struct i915_execbuffer *eb)
 				list_add_tail(&ev->unbound_link, &last);
 		}
 		list_splice_tail(&last, &unbound);
-		mutex_unlock(&eb->i915->drm.struct_mutex);
+		GEM_BUG_ON(!count);
 
-		if (err == -EAGAIN) {
-			flush_workqueue(eb->i915->mm.userptr_wq);
-			continue;
-		}
-
-		switch (pass++) {
-		case 0:
-			break;
-
-		case 1:
-			/*
-			 * Too fragmented, retire everything on the timeline
-			 * and so make it all [contexts included] available to
-			 * evict.
-			 */
-			err = wait_for_timeline(eb->context->timeline);
-			if (err)
-				return err;
+		if (signal_pending(current))
+			return -EINTR;
 
-			break;
+		/* Now safe to wait with no reservations held */
 
-		default:
-			return -ENOSPC;
+		if (err == -EAGAIN) {
+			flush_workqueue(eb->i915->mm.userptr_wq);
+			pass = 0;
 		}
 
-		pin_flags = PIN_USER;
+		err = wait_for_unbinds(eb, &unbound, pass++);
+		if (err)
+			return err;
 	} while (1);
 }
 
@@ -1418,6 +2099,29 @@ relocate_entry(struct i915_execbuffer *eb,
 	return target->node.start | UPDATE;
 }
 
+static int gen6_fixup_ggtt(struct i915_vma *vma)
+{
+	int err;
+
+	if (i915_vma_is_bound(vma, I915_VMA_GLOBAL_BIND))
+		return 0;
+
+	err = i915_vma_wait_for_bind(vma);
+	if (err)
+		return err;
+
+	mutex_lock(&vma->vm->mutex);
+	if (!(atomic_fetch_or(I915_VMA_GLOBAL_BIND, &vma->flags) & I915_VMA_GLOBAL_BIND)) {
+		__i915_gem_object_pin_pages(vma->obj);
+		vma->ops->bind_vma(vma->vm, NULL, vma,
+				   vma->obj->cache_level,
+				   I915_VMA_GLOBAL_BIND);
+	}
+	mutex_unlock(&vma->vm->mutex);
+
+	return 0;
+}
+
 static u64
 eb_relocate_entry(struct i915_execbuffer *eb,
 		  struct eb_vma *ev,
@@ -1432,6 +2136,8 @@ eb_relocate_entry(struct i915_execbuffer *eb,
 	if (unlikely(!target))
 		return -ENOENT;
 
+	GEM_BUG_ON(!i915_vma_is_pinned(target->vma));
+
 	/* Validate that the target is in a valid r/w GPU domain */
 	if (unlikely(reloc->write_domain & (reloc->write_domain - 1))) {
 		drm_dbg(&i915->drm, "reloc with multiple write domains: "
@@ -1466,9 +2172,7 @@ eb_relocate_entry(struct i915_execbuffer *eb,
 		 */
 		if (reloc->write_domain == I915_GEM_DOMAIN_INSTRUCTION &&
 		    IS_GEN(eb->i915, 6)) {
-			err = i915_vma_bind(target->vma,
-					    target->vma->obj->cache_level,
-					    PIN_GLOBAL, NULL);
+			err = gen6_fixup_ggtt(target->vma);
 			if (err)
 				return err;
 		}
@@ -1642,6 +2346,8 @@ static int eb_move_to_gpu(struct i915_execbuffer *eb)
 		struct drm_i915_gem_object *obj = vma->obj;
 
 		assert_vma_held(vma);
+		GEM_BUG_ON(!(flags & __EXEC_OBJECT_HAS_PIN));
+		GEM_BUG_ON(!i915_vma_is_bound(vma, I915_VMA_LOCAL_BIND));
 
 		if (flags & EXEC_OBJECT_CAPTURE) {
 			struct i915_capture_list *capture;
@@ -1680,7 +2386,6 @@ static int eb_move_to_gpu(struct i915_execbuffer *eb)
 			err = i915_vma_move_to_active(vma, eb->request, flags);
 
 		i915_vma_unlock(vma);
-		eb_unreserve_vma(ev);
 	}
 	ww_acquire_fini(&acquire);
 
@@ -2623,7 +3328,7 @@ i915_gem_do_execbuffer(struct drm_device *dev,
 	 * snb/ivb/vlv conflate the "batch in ppgtt" bit with the "non-secure
 	 * batch" bit. Hence we need to pin secure batches into the global gtt.
 	 * hsw should have this fixed, but bdw mucks it up again. */
-	batch = eb.batch->vma;
+	batch = i915_vma_get(eb.batch->vma);
 	if (eb.batch_flags & I915_DISPATCH_SECURE) {
 		struct i915_vma *vma;
 
@@ -2643,6 +3348,7 @@ i915_gem_do_execbuffer(struct drm_device *dev,
 			goto err_parse;
 		}
 
+		GEM_BUG_ON(vma->obj != batch->obj);
 		batch = vma;
 	}
 
@@ -2722,6 +3428,7 @@ i915_gem_do_execbuffer(struct drm_device *dev,
 err_parse:
 	if (batch->private)
 		intel_gt_buffer_pool_put(batch->private);
+	i915_vma_put(batch);
 err_vma:
 	if (eb.trampoline)
 		i915_vma_unpin(eb.trampoline);
diff --git a/drivers/gpu/drm/i915/gt/gen6_ppgtt.c b/drivers/gpu/drm/i915/gt/gen6_ppgtt.c
index 1c1e807f674d..1bfc7b9101e7 100644
--- a/drivers/gpu/drm/i915/gt/gen6_ppgtt.c
+++ b/drivers/gpu/drm/i915/gt/gen6_ppgtt.c
@@ -359,6 +359,7 @@ static struct i915_vma *pd_vma_create(struct gen6_ppgtt *ppgtt, int size)
 	atomic_set(&vma->flags, I915_VMA_GGTT);
 	vma->ggtt_view.type = I915_GGTT_VIEW_ROTATED; /* prevent fencing */
 
+	INIT_LIST_HEAD(&vma->vm_link);
 	INIT_LIST_HEAD(&vma->obj_link);
 	INIT_LIST_HEAD(&vma->closed_link);
 
diff --git a/drivers/gpu/drm/i915/gt/intel_gtt.c b/drivers/gpu/drm/i915/gt/intel_gtt.c
index 5df35e5a8936..8c044e6b4880 100644
--- a/drivers/gpu/drm/i915/gt/intel_gtt.c
+++ b/drivers/gpu/drm/i915/gt/intel_gtt.c
@@ -55,6 +55,8 @@ void __i915_vm_close(struct i915_address_space *vm)
 
 void i915_address_space_fini(struct i915_address_space *vm)
 {
+	i915_active_fini(&vm->binding);
+
 	drm_mm_takedown(&vm->mm);
 	mutex_destroy(&vm->mutex);
 }
@@ -100,6 +102,8 @@ void i915_address_space_init(struct i915_address_space *vm, int subclass)
 	drm_mm_init(&vm->mm, 0, vm->total);
 	vm->mm.head_node.color = I915_COLOR_UNEVICTABLE;
 
+	i915_active_init(&vm->binding, NULL, NULL);
+
 	INIT_LIST_HEAD(&vm->bound_list);
 }
 
diff --git a/drivers/gpu/drm/i915/gt/intel_gtt.h b/drivers/gpu/drm/i915/gt/intel_gtt.h
index 6e1bedf5448b..7ed804027ba1 100644
--- a/drivers/gpu/drm/i915/gt/intel_gtt.h
+++ b/drivers/gpu/drm/i915/gt/intel_gtt.h
@@ -250,6 +250,8 @@ struct i915_address_space {
 	 */
 	struct list_head bound_list;
 
+	struct i915_active binding;
+
 	/* Global GTT */
 	bool is_ggtt:1;
 
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 9aa3066cb75d..e998f25f30a3 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -997,6 +997,9 @@ i915_gem_object_ggtt_pin(struct drm_i915_gem_object *obj,
 		return vma;
 
 	if (i915_vma_misplaced(vma, size, alignment, flags)) {
+		if (flags & PIN_NOEVICT)
+			return ERR_PTR(-ENOSPC);
+
 		if (flags & PIN_NONBLOCK) {
 			if (i915_vma_is_pinned(vma) || i915_vma_is_active(vma))
 				return ERR_PTR(-ENOSPC);
@@ -1016,6 +1019,10 @@ i915_gem_object_ggtt_pin(struct drm_i915_gem_object *obj,
 			return ERR_PTR(ret);
 	}
 
+	if (flags & PIN_NONBLOCK &&
+	    i915_active_fence_isset(&vma->active.excl))
+		return ERR_PTR(-EAGAIN);
+
 	ret = i915_vma_pin(vma, size, alignment, flags | PIN_GLOBAL);
 	if (ret)
 		return ERR_PTR(ret);
diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
index cb43381b0d37..7e1225874b03 100644
--- a/drivers/gpu/drm/i915/i915_gem_gtt.c
+++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
@@ -219,6 +219,8 @@ int i915_gem_gtt_insert(struct i915_address_space *vm,
 		mode = DRM_MM_INSERT_HIGHEST;
 	if (flags & PIN_MAPPABLE)
 		mode = DRM_MM_INSERT_LOW;
+	if (flags & PIN_NOSEARCH)
+		mode |= DRM_MM_INSERT_ONCE;
 
 	/* We only allocate in PAGE_SIZE/GTT_PAGE_SIZE (4096) chunks,
 	 * so we know that we always have a minimum alignment of 4096.
@@ -236,6 +238,9 @@ int i915_gem_gtt_insert(struct i915_address_space *vm,
 	if (err != -ENOSPC)
 		return err;
 
+	if (flags & PIN_NOSEARCH)
+		return -ENOSPC;
+
 	if (mode & DRM_MM_INSERT_ONCE) {
 		err = drm_mm_insert_node_in_range(&vm->mm, node,
 						  size, alignment, color,
diff --git a/drivers/gpu/drm/i915/i915_vma.c b/drivers/gpu/drm/i915/i915_vma.c
index 0c9af30fc3d6..cbfb11310846 100644
--- a/drivers/gpu/drm/i915/i915_vma.c
+++ b/drivers/gpu/drm/i915/i915_vma.c
@@ -133,6 +133,7 @@ vma_create(struct drm_i915_gem_object *obj,
 		fs_reclaim_release(GFP_KERNEL);
 	}
 
+	INIT_LIST_HEAD(&vma->vm_link);
 	INIT_LIST_HEAD(&vma->closed_link);
 
 	if (view && view->type != I915_GGTT_VIEW_NORMAL) {
@@ -341,25 +342,37 @@ struct i915_vma_work *i915_vma_work(void)
 	return vw;
 }
 
-int i915_vma_wait_for_bind(struct i915_vma *vma)
+static int
+__i915_vma_wait_excl(struct i915_vma *vma, bool bound, unsigned int flags)
 {
+	struct dma_fence *fence;
 	int err = 0;
 
-	if (rcu_access_pointer(vma->active.excl.fence)) {
-		struct dma_fence *fence;
+	fence = i915_active_fence_get(&vma->active.excl);
+	if (!fence)
+		return 0;
 
-		rcu_read_lock();
-		fence = dma_fence_get_rcu_safe(&vma->active.excl.fence);
-		rcu_read_unlock();
-		if (fence) {
-			err = dma_fence_wait(fence, MAX_SCHEDULE_TIMEOUT);
-			dma_fence_put(fence);
-		}
+	if (drm_mm_node_allocated(&vma->node) == bound) {
+		if (flags & PIN_NOEVICT)
+			err = -EBUSY;
+		else
+			err = dma_fence_wait(fence, true);
 	}
 
+	dma_fence_put(fence);
 	return err;
 }
 
+int i915_vma_wait_for_bind(struct i915_vma *vma)
+{
+	return __i915_vma_wait_excl(vma, true, 0);
+}
+
+int i915_vma_wait_for_unbind(struct i915_vma *vma)
+{
+	return __i915_vma_wait_excl(vma, false, 0);
+}
+
 /**
  * i915_vma_bind - Sets up PTEs for an VMA in it's corresponding address space.
  * @vma: VMA to map
@@ -624,8 +637,9 @@ i915_vma_insert(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
 	u64 start, end;
 	int ret;
 
-	GEM_BUG_ON(i915_vma_is_bound(vma, I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND));
+	GEM_BUG_ON(i915_vma_is_bound(vma, I915_VMA_BIND_MASK));
 	GEM_BUG_ON(drm_mm_node_allocated(&vma->node));
+	GEM_BUG_ON(i915_active_fence_isset(&vma->active.excl));
 
 	size = max(size, vma->size);
 	alignment = max(alignment, vma->display_alignment);
@@ -721,7 +735,7 @@ i915_vma_insert(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
 	GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
 	GEM_BUG_ON(!i915_gem_valid_gtt_space(vma, color));
 
-	list_add_tail(&vma->vm_link, &vma->vm->bound_list);
+	list_move_tail(&vma->vm_link, &vma->vm->bound_list);
 
 	return 0;
 }
@@ -729,15 +743,12 @@ i915_vma_insert(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
 static void
 i915_vma_detach(struct i915_vma *vma)
 {
-	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));
-
 	/*
 	 * And finally now the object is completely decoupled from this
 	 * vma, we can drop its hold on the backing storage and allow
 	 * it to be reaped by the shrinker.
 	 */
-	list_del(&vma->vm_link);
+	list_del_init(&vma->vm_link);
 }
 
 bool i915_vma_pin_inplace(struct i915_vma *vma, unsigned int flags)
@@ -785,7 +796,7 @@ bool i915_vma_pin_inplace(struct i915_vma *vma, unsigned int flags)
 	return pinned;
 }
 
-static int vma_get_pages(struct i915_vma *vma)
+int i915_vma_get_pages(struct i915_vma *vma)
 {
 	int err = 0;
 
@@ -832,7 +843,7 @@ static void __vma_put_pages(struct i915_vma *vma, unsigned int count)
 	mutex_unlock(&vma->pages_mutex);
 }
 
-static void vma_put_pages(struct i915_vma *vma)
+void i915_vma_put_pages(struct i915_vma *vma)
 {
 	if (atomic_add_unless(&vma->pages_count, -1, 1))
 		return;
@@ -849,9 +860,13 @@ static void vma_unbind_pages(struct i915_vma *vma)
 	/* 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);
+	if (count)
+		__vma_put_pages(vma, count | count << I915_VMA_PAGES_BIAS);
+}
 
-	__vma_put_pages(vma, count | count << I915_VMA_PAGES_BIAS);
+static int __wait_for_unbind(struct i915_vma *vma, unsigned int flags)
+{
+	return __i915_vma_wait_excl(vma, false, flags);
 }
 
 int i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
@@ -870,13 +885,17 @@ int i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
 	if (i915_vma_pin_inplace(vma, flags & I915_VMA_BIND_MASK))
 		return 0;
 
-	err = vma_get_pages(vma);
+	err = i915_vma_get_pages(vma);
 	if (err)
 		return err;
 
 	if (flags & PIN_GLOBAL)
 		wakeref = intel_runtime_pm_get(&vma->vm->i915->runtime_pm);
 
+	err = __wait_for_unbind(vma, flags);
+	if (err)
+		goto err_rpm;
+
 	if (flags & vma->vm->bind_async_flags) {
 		u64 max_size;
 
@@ -951,6 +970,10 @@ int i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
 		goto err_unlock;
 
 	if (!(bound & I915_VMA_BIND_MASK)) {
+		err = __wait_for_unbind(vma, flags);
+		if (err)
+			goto err_active;
+
 		err = i915_vma_insert(vma, size, alignment, flags);
 		if (err)
 			goto err_active;
@@ -970,6 +993,7 @@ int i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
 	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);
+	GEM_BUG_ON(!i915_vma_is_active(vma));
 
 	__i915_vma_pin(vma);
 	GEM_BUG_ON(!i915_vma_is_pinned(vma));
@@ -991,7 +1015,7 @@ int i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
 err_rpm:
 	if (wakeref)
 		intel_runtime_pm_put(&vma->vm->i915->runtime_pm, wakeref);
-	vma_put_pages(vma);
+	i915_vma_put_pages(vma);
 	return err;
 }
 
@@ -1095,6 +1119,7 @@ void i915_vma_release(struct kref *ref)
 		GEM_BUG_ON(drm_mm_node_allocated(&vma->node));
 	}
 	GEM_BUG_ON(i915_vma_is_active(vma));
+	GEM_BUG_ON(!list_empty(&vma->vm_link));
 
 	if (vma->obj) {
 		struct drm_i915_gem_object *obj = vma->obj;
@@ -1154,7 +1179,7 @@ static void __i915_vma_iounmap(struct i915_vma *vma)
 {
 	GEM_BUG_ON(i915_vma_is_pinned(vma));
 
-	if (vma->iomap == NULL)
+	if (!vma->iomap)
 		return;
 
 	io_mapping_unmap(vma->iomap);
diff --git a/drivers/gpu/drm/i915/i915_vma.h b/drivers/gpu/drm/i915/i915_vma.h
index 03fea54fd573..9a26e6cbe8cd 100644
--- a/drivers/gpu/drm/i915/i915_vma.h
+++ b/drivers/gpu/drm/i915/i915_vma.h
@@ -236,6 +236,9 @@ static inline void i915_vma_unlock(struct i915_vma *vma)
 	dma_resv_unlock(vma->resv);
 }
 
+int i915_vma_get_pages(struct i915_vma *vma);
+void i915_vma_put_pages(struct i915_vma *vma);
+
 bool i915_vma_pin_inplace(struct i915_vma *vma, unsigned int flags);
 
 int __must_check
@@ -379,6 +382,7 @@ void i915_vma_make_shrinkable(struct i915_vma *vma);
 void i915_vma_make_purgeable(struct i915_vma *vma);
 
 int i915_vma_wait_for_bind(struct i915_vma *vma);
+int i915_vma_wait_for_unbind(struct i915_vma *vma);
 
 static inline int i915_vma_sync(struct i915_vma *vma)
 {
-- 
2.20.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [Intel-gfx] [PATCH 13/20] drm/i915/gem: Bind the fence async for execbuf
  2020-07-06  6:19 [Intel-gfx] s/obj->mm.lock// Chris Wilson
                   ` (11 preceding siblings ...)
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 12/20] drm/i915/gem: Asynchronous GTT unbinding Chris Wilson
@ 2020-07-06  6:19 ` Chris Wilson
  2020-07-14 12:19   ` Tvrtko Ursulin
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 14/20] drm/i915/gem: Include cmdparser in common execbuf pinning Chris Wilson
                   ` (11 subsequent siblings)
  24 siblings, 1 reply; 65+ messages in thread
From: Chris Wilson @ 2020-07-06  6:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: Chris Wilson

It is illegal to wait on an another vma while holding the vm->mutex, as
that easily leads to ABBA deadlocks (we wait on a second vma that waits
on us to release the vm->mutex). So while the vm->mutex exists, move the
waiting outside of the lock into the async binding pipeline.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 .../gpu/drm/i915/gem/i915_gem_execbuffer.c    |  21 +--
 drivers/gpu/drm/i915/gt/intel_ggtt_fencing.c  | 137 +++++++++++++++++-
 drivers/gpu/drm/i915/gt/intel_ggtt_fencing.h  |   5 +
 3 files changed, 151 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
index 6a406e8798ef..c14c3b7e0dfd 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
@@ -1056,15 +1056,6 @@ static int eb_reserve_vma(struct eb_vm_work *work, struct eb_bind_vma *bind)
 		return err;
 
 pin:
-	if (unlikely(exec_flags & EXEC_OBJECT_NEEDS_FENCE)) {
-		err = __i915_vma_pin_fence(vma); /* XXX no waiting */
-		if (unlikely(err))
-			return err;
-
-		if (vma->fence)
-			bind->ev->flags |= __EXEC_OBJECT_HAS_FENCE;
-	}
-
 	bind_flags &= ~atomic_read(&vma->flags);
 	if (bind_flags) {
 		err = set_bind_fence(vma, work);
@@ -1095,6 +1086,15 @@ static int eb_reserve_vma(struct eb_vm_work *work, struct eb_bind_vma *bind)
 	bind->ev->flags |= __EXEC_OBJECT_HAS_PIN;
 	GEM_BUG_ON(eb_vma_misplaced(entry, vma, bind->ev->flags));
 
+	if (unlikely(exec_flags & EXEC_OBJECT_NEEDS_FENCE)) {
+		err = __i915_vma_pin_fence_async(vma, &work->base);
+		if (unlikely(err))
+			return err;
+
+		if (vma->fence)
+			bind->ev->flags |= __EXEC_OBJECT_HAS_FENCE;
+	}
+
 	return 0;
 }
 
@@ -1160,6 +1160,9 @@ static void __eb_bind_vma(struct eb_vm_work *work)
 		struct eb_bind_vma *bind = &work->bind[n];
 		struct i915_vma *vma = bind->ev->vma;
 
+		if (bind->ev->flags & __EXEC_OBJECT_HAS_FENCE)
+			__i915_vma_apply_fence_async(vma);
+
 		if (!bind->bind_flags)
 			goto put;
 
diff --git a/drivers/gpu/drm/i915/gt/intel_ggtt_fencing.c b/drivers/gpu/drm/i915/gt/intel_ggtt_fencing.c
index 7fb36b12fe7a..734b6aa61809 100644
--- a/drivers/gpu/drm/i915/gt/intel_ggtt_fencing.c
+++ b/drivers/gpu/drm/i915/gt/intel_ggtt_fencing.c
@@ -21,10 +21,13 @@
  * IN THE SOFTWARE.
  */
 
+#include "i915_active.h"
 #include "i915_drv.h"
 #include "i915_scatterlist.h"
+#include "i915_sw_fence_work.h"
 #include "i915_pvinfo.h"
 #include "i915_vgpu.h"
+#include "i915_vma.h"
 
 /**
  * DOC: fence register handling
@@ -340,19 +343,37 @@ static struct i915_fence_reg *fence_find(struct i915_ggtt *ggtt)
 	return ERR_PTR(-EDEADLK);
 }
 
+static int fence_wait_bind(struct i915_fence_reg *reg)
+{
+	struct dma_fence *fence;
+	int err = 0;
+
+	fence = i915_active_fence_get(&reg->active.excl);
+	if (fence) {
+		err = dma_fence_wait(fence, true);
+		dma_fence_put(fence);
+	}
+
+	return err;
+}
+
 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_fence_reg *fence = vma->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;
+	if (fence) {
 		GEM_BUG_ON(fence->vma != vma);
+
+		err = fence_wait_bind(fence);
+		if (err)
+			return err;
+
 		atomic_inc(&fence->pin_count);
 		if (!fence->dirty) {
 			list_move_tail(&fence->link, &ggtt->fence_list);
@@ -384,6 +405,116 @@ int __i915_vma_pin_fence(struct i915_vma *vma)
 	return err;
 }
 
+static int set_bind_fence(struct i915_fence_reg *fence,
+			  struct dma_fence_work *work)
+{
+	struct dma_fence *prev;
+	int err;
+
+	if (rcu_access_pointer(fence->active.excl.fence) == &work->dma)
+		return 0;
+
+	err = i915_sw_fence_await_active(&work->chain,
+					 &fence->active,
+					 I915_ACTIVE_AWAIT_ACTIVE);
+	if (err)
+		return err;
+
+	if (i915_active_acquire(&fence->active))
+		return -ENOENT;
+
+	prev = i915_active_set_exclusive(&fence->active, &work->dma);
+	if (unlikely(prev)) {
+		err = i915_sw_fence_await_dma_fence(&work->chain, prev, 0,
+						    GFP_NOWAIT | __GFP_NOWARN);
+		dma_fence_put(prev);
+	}
+
+	i915_active_release(&fence->active);
+	return err < 0 ? err : 0;
+}
+
+int __i915_vma_pin_fence_async(struct i915_vma *vma,
+			       struct dma_fence_work *work)
+{
+	struct i915_ggtt *ggtt = i915_vm_to_ggtt(vma->vm);
+	struct i915_vma *set = i915_gem_object_is_tiled(vma->obj) ? vma : NULL;
+	struct i915_fence_reg *fence = vma->fence;
+	int err;
+
+	lockdep_assert_held(&vma->vm->mutex);
+
+	/* Just update our place in the LRU if our fence is getting reused. */
+	if (fence) {
+		GEM_BUG_ON(fence->vma != vma);
+		GEM_BUG_ON(!i915_vma_is_map_and_fenceable(vma));
+	} else if (set) {
+		if (!i915_vma_is_map_and_fenceable(vma))
+			return -EINVAL;
+
+		fence = fence_find(ggtt);
+		if (IS_ERR(fence))
+			return -ENOSPC;
+
+		GEM_BUG_ON(atomic_read(&fence->pin_count));
+		fence->dirty = true;
+	} else {
+		return 0;
+	}
+
+	atomic_inc(&fence->pin_count);
+	list_move_tail(&fence->link, &ggtt->fence_list);
+	if (!fence->dirty)
+		return 0;
+
+	if (INTEL_GEN(fence_to_i915(fence)) < 4 &&
+	    rcu_access_pointer(vma->active.excl.fence) != &work->dma) {
+		/* implicit 'unfenced' GPU blits */
+		err = i915_sw_fence_await_active(&work->chain,
+						 &vma->active,
+						 I915_ACTIVE_AWAIT_ACTIVE);
+		if (err)
+			goto err_unpin;
+	}
+
+	err = set_bind_fence(fence, work);
+	if (err)
+		goto err_unpin;
+
+	if (set) {
+		fence->start = vma->node.start;
+		fence->size  = vma->fence_size;
+		fence->stride = i915_gem_object_get_stride(vma->obj);
+		fence->tiling = i915_gem_object_get_tiling(vma->obj);
+
+		vma->fence = fence;
+	} else {
+		fence->tiling = 0;
+		vma->fence = NULL;
+	}
+
+	set = xchg(&fence->vma, set);
+	if (set && set != vma) {
+		GEM_BUG_ON(set->fence != fence);
+		WRITE_ONCE(set->fence, NULL);
+		i915_vma_revoke_mmap(set);
+	}
+
+	return 0;
+
+err_unpin:
+	atomic_dec(&fence->pin_count);
+	return err;
+}
+
+void __i915_vma_apply_fence_async(struct i915_vma *vma)
+{
+	struct i915_fence_reg *fence = vma->fence;
+
+	if (fence->dirty)
+		fence_write(fence);
+}
+
 /**
  * i915_vma_pin_fence - set up fencing for a vma
  * @vma: vma to map through a fence reg
diff --git a/drivers/gpu/drm/i915/gt/intel_ggtt_fencing.h b/drivers/gpu/drm/i915/gt/intel_ggtt_fencing.h
index 9eef679e1311..d306ac14d47e 100644
--- a/drivers/gpu/drm/i915/gt/intel_ggtt_fencing.h
+++ b/drivers/gpu/drm/i915/gt/intel_ggtt_fencing.h
@@ -30,6 +30,7 @@
 
 #include "i915_active.h"
 
+struct dma_fence_work;
 struct drm_i915_gem_object;
 struct i915_ggtt;
 struct i915_vma;
@@ -70,6 +71,10 @@ void i915_gem_object_do_bit_17_swizzle(struct drm_i915_gem_object *obj,
 void i915_gem_object_save_bit_17_swizzle(struct drm_i915_gem_object *obj,
 					 struct sg_table *pages);
 
+int __i915_vma_pin_fence_async(struct i915_vma *vma,
+			       struct dma_fence_work *work);
+void __i915_vma_apply_fence_async(struct i915_vma *vma);
+
 void intel_ggtt_init_fences(struct i915_ggtt *ggtt);
 void intel_ggtt_fini_fences(struct i915_ggtt *ggtt);
 
-- 
2.20.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [Intel-gfx] [PATCH 14/20] drm/i915/gem: Include cmdparser in common execbuf pinning
  2020-07-06  6:19 [Intel-gfx] s/obj->mm.lock// Chris Wilson
                   ` (12 preceding siblings ...)
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 13/20] drm/i915/gem: Bind the fence async for execbuf Chris Wilson
@ 2020-07-06  6:19 ` Chris Wilson
  2020-07-14 12:48   ` Tvrtko Ursulin
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 15/20] drm/i915/gem: Include secure batch " Chris Wilson
                   ` (10 subsequent siblings)
  24 siblings, 1 reply; 65+ messages in thread
From: Chris Wilson @ 2020-07-06  6:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: Chris Wilson

Pull the cmdparser allocations in to the reservation phase, and then
they are included in the common vma pinning pass.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 .../gpu/drm/i915/gem/i915_gem_execbuffer.c    | 348 ++++++++++--------
 drivers/gpu/drm/i915/gem/i915_gem_object.h    |  10 +
 drivers/gpu/drm/i915/i915_cmd_parser.c        |  21 +-
 3 files changed, 218 insertions(+), 161 deletions(-)

diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
index c14c3b7e0dfd..8e4681427ce3 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
@@ -25,6 +25,7 @@
 #include "i915_gem_clflush.h"
 #include "i915_gem_context.h"
 #include "i915_gem_ioctls.h"
+#include "i915_memcpy.h"
 #include "i915_sw_fence_work.h"
 #include "i915_trace.h"
 
@@ -52,6 +53,7 @@ struct eb_bind_vma {
 
 struct eb_vma_array {
 	struct kref kref;
+	struct list_head aux_list;
 	struct eb_vma vma[];
 };
 
@@ -246,7 +248,6 @@ struct i915_execbuffer {
 
 	struct i915_request *request; /** our request to build */
 	struct eb_vma *batch; /** identity of the batch obj/vma */
-	struct i915_vma *trampoline; /** trampoline used for chaining */
 
 	/** actual size of execobj[] as we may extend it for the cmdparser */
 	unsigned int buffer_count;
@@ -281,6 +282,11 @@ struct i915_execbuffer {
 		unsigned int rq_size;
 	} reloc_cache;
 
+	struct eb_cmdparser {
+		struct eb_vma *shadow;
+		struct eb_vma *trampoline;
+	} parser;
+
 	u64 invalid_flags; /** Set of execobj.flags that are invalid */
 	u32 context_flags; /** Set of execobj.flags to insert from the ctx */
 
@@ -298,6 +304,10 @@ struct i915_execbuffer {
 	struct eb_vma_array *array;
 };
 
+static struct drm_i915_gem_exec_object2 no_entry = {
+	.offset = -1ull
+};
+
 static inline bool eb_use_cmdparser(const struct i915_execbuffer *eb)
 {
 	return intel_engine_requires_cmd_parser(eb->engine) ||
@@ -314,6 +324,7 @@ static struct eb_vma_array *eb_vma_array_create(unsigned int count)
 		return NULL;
 
 	kref_init(&arr->kref);
+	INIT_LIST_HEAD(&arr->aux_list);
 	arr->vma[0].vma = NULL;
 
 	return arr;
@@ -339,16 +350,31 @@ static inline void eb_unreserve_vma(struct eb_vma *ev)
 		       __EXEC_OBJECT_HAS_FENCE);
 }
 
+static void eb_vma_destroy(struct eb_vma *ev)
+{
+	eb_unreserve_vma(ev);
+	i915_vma_put(ev->vma);
+}
+
+static void eb_destroy_aux(struct eb_vma_array *arr)
+{
+	struct eb_vma *ev, *en;
+
+	list_for_each_entry_safe(ev, en, &arr->aux_list, reloc_link) {
+		eb_vma_destroy(ev);
+		kfree(ev);
+	}
+}
+
 static void eb_vma_array_destroy(struct kref *kref)
 {
 	struct eb_vma_array *arr = container_of(kref, typeof(*arr), kref);
-	struct eb_vma *ev = arr->vma;
+	struct eb_vma *ev;
 
-	while (ev->vma) {
-		eb_unreserve_vma(ev);
-		i915_vma_put(ev->vma);
-		ev++;
-	}
+	eb_destroy_aux(arr);
+
+	for (ev = arr->vma; ev->vma; ev++)
+		eb_vma_destroy(ev);
 
 	kvfree(arr);
 }
@@ -396,8 +422,8 @@ eb_lock_vma(struct i915_execbuffer *eb, struct ww_acquire_ctx *acquire)
 
 static int eb_create(struct i915_execbuffer *eb)
 {
-	/* Allocate an extra slot for use by the command parser + sentinel */
-	eb->array = eb_vma_array_create(eb->buffer_count + 2);
+	/* Allocate an extra slot for use by the sentinel */
+	eb->array = eb_vma_array_create(eb->buffer_count + 1);
 	if (!eb->array)
 		return -ENOMEM;
 
@@ -1078,7 +1104,7 @@ static int eb_reserve_vma(struct eb_vm_work *work, struct eb_bind_vma *bind)
 	GEM_BUG_ON(!(drm_mm_node_allocated(&vma->node) ^
 		     drm_mm_node_allocated(&bind->hole)));
 
-	if (entry->offset != vma->node.start) {
+	if (entry != &no_entry && entry->offset != vma->node.start) {
 		entry->offset = vma->node.start | UPDATE;
 		*work->p_flags |= __EXEC_HAS_RELOC;
 	}
@@ -1374,7 +1400,8 @@ static int eb_reserve_vm(struct i915_execbuffer *eb)
 		struct i915_vma *vma = ev->vma;
 
 		if (eb_pin_vma_inplace(eb, entry, ev)) {
-			if (entry->offset != vma->node.start) {
+			if (entry != &no_entry &&
+			    entry->offset != vma->node.start) {
 				entry->offset = vma->node.start | UPDATE;
 				eb->args->flags |= __EXEC_HAS_RELOC;
 			}
@@ -1514,6 +1541,113 @@ static int eb_reserve_vm(struct i915_execbuffer *eb)
 	} while (1);
 }
 
+static int eb_alloc_cmdparser(struct i915_execbuffer *eb)
+{
+	struct intel_gt_buffer_pool_node *pool;
+	struct i915_vma *vma;
+	struct eb_vma *ev;
+	unsigned int len;
+	int err;
+
+	if (range_overflows_t(u64,
+			      eb->batch_start_offset, eb->batch_len,
+			      eb->batch->vma->size)) {
+		drm_dbg(&eb->i915->drm,
+			"Attempting to use out-of-bounds batch\n");
+		return -EINVAL;
+	}
+
+	if (eb->batch_len == 0)
+		eb->batch_len = eb->batch->vma->size - eb->batch_start_offset;
+
+	if (!eb_use_cmdparser(eb))
+		return 0;
+
+	len = eb->batch_len;
+	if (!CMDPARSER_USES_GGTT(eb->i915)) {
+		/*
+		 * ppGTT backed shadow buffers must be mapped RO, to prevent
+		 * post-scan tampering
+		 */
+		if (!eb->context->vm->has_read_only) {
+			drm_dbg(&eb->i915->drm,
+				"Cannot prevent post-scan tampering without RO capable vm\n");
+			return -EINVAL;
+		}
+	} else {
+		len += I915_CMD_PARSER_TRAMPOLINE_SIZE;
+	}
+
+	pool = intel_gt_get_buffer_pool(eb->engine->gt, len);
+	if (IS_ERR(pool))
+		return PTR_ERR(pool);
+
+	ev = kzalloc(sizeof(*ev), GFP_KERNEL);
+	if (!ev) {
+		err = -ENOMEM;
+		goto err_pool;
+	}
+
+	vma = i915_vma_instance(pool->obj, eb->context->vm, NULL);
+	if (IS_ERR(vma)) {
+		err = PTR_ERR(vma);
+		goto err_ev;
+	}
+	i915_gem_object_set_readonly(vma->obj);
+	i915_gem_object_set_cache_coherency(vma->obj, I915_CACHE_LLC);
+	vma->private = pool;
+
+	ev->vma = i915_vma_get(vma);
+	ev->exec = &no_entry;
+	list_add(&ev->reloc_link, &eb->array->aux_list);
+	list_add(&ev->bind_link, &eb->bind_list);
+	list_add(&ev->submit_link, &eb->submit_list);
+
+	if (CMDPARSER_USES_GGTT(eb->i915)) {
+		eb->parser.trampoline = ev;
+
+		/*
+		 * Special care when binding will be required for full-ppgtt
+		 * as there will be distinct vm involved, and we will need to
+		 * separate the binding/eviction passes (different vm->mutex).
+		 */
+		if (GEM_WARN_ON(eb->context->vm != &eb->engine->gt->ggtt->vm)) {
+			ev = kzalloc(sizeof(*ev), GFP_KERNEL);
+			if (!ev) {
+				err = -ENOMEM;
+				goto err_pool;
+			}
+
+			vma = i915_vma_instance(pool->obj,
+						&eb->engine->gt->ggtt->vm,
+						NULL);
+			if (IS_ERR(vma)) {
+				err = PTR_ERR(vma);
+				goto err_ev;
+			}
+			vma->private = pool;
+
+			ev->vma = i915_vma_get(vma);
+			ev->exec = &no_entry;
+			list_add(&ev->reloc_link, &eb->array->aux_list);
+			list_add(&ev->bind_link, &eb->bind_list);
+			list_add(&ev->submit_link, &eb->submit_list);
+		}
+
+		ev->flags = EXEC_OBJECT_NEEDS_GTT;
+		eb->batch_flags |= I915_DISPATCH_SECURE;
+	}
+
+	eb->parser.shadow = ev;
+	return 0;
+
+err_ev:
+	kfree(ev);
+err_pool:
+	intel_gt_buffer_pool_put(pool);
+	return err;
+}
+
 static unsigned int eb_batch_index(const struct i915_execbuffer *eb)
 {
 	if (eb->args->flags & I915_EXEC_BATCH_FIRST)
@@ -1684,9 +1818,7 @@ static void eb_destroy(const struct i915_execbuffer *eb)
 {
 	GEM_BUG_ON(eb->reloc_cache.rq);
 
-	if (eb->array)
-		eb_vma_array_put(eb->array);
-
+	eb_vma_array_put(eb->array);
 	if (eb->lut_size > 0)
 		kfree(eb->buckets);
 }
@@ -2306,6 +2438,10 @@ static int eb_relocate(struct i915_execbuffer *eb)
 	if (err)
 		return err;
 
+	err = eb_alloc_cmdparser(eb);
+	if (err)
+		return err;
+
 	err = eb_reserve_vm(eb);
 	if (err)
 		return err;
@@ -2392,8 +2528,6 @@ static int eb_move_to_gpu(struct i915_execbuffer *eb)
 	}
 	ww_acquire_fini(&acquire);
 
-	eb_vma_array_put(fetch_and_zero(&eb->array));
-
 	if (unlikely(err))
 		goto err_skip;
 
@@ -2457,25 +2591,6 @@ static int i915_reset_gen7_sol_offsets(struct i915_request *rq)
 	return 0;
 }
 
-static struct i915_vma *
-shadow_batch_pin(struct drm_i915_gem_object *obj,
-		 struct i915_address_space *vm,
-		 unsigned int flags)
-{
-	struct i915_vma *vma;
-	int err;
-
-	vma = i915_vma_instance(obj, vm, NULL);
-	if (IS_ERR(vma))
-		return vma;
-
-	err = i915_vma_pin(vma, 0, 0, flags);
-	if (err)
-		return ERR_PTR(err);
-
-	return vma;
-}
-
 struct eb_parse_work {
 	struct dma_fence_work base;
 	struct intel_engine_cs *engine;
@@ -2502,6 +2617,9 @@ static void __eb_parse_release(struct dma_fence_work *work)
 {
 	struct eb_parse_work *pw = container_of(work, typeof(*pw), base);
 
+	i915_gem_object_unpin_pages(pw->shadow->obj);
+	i915_gem_object_unpin_pages(pw->batch->obj);
+
 	if (pw->trampoline)
 		i915_active_release(&pw->trampoline->active);
 	i915_active_release(&pw->shadow->active);
@@ -2527,35 +2645,48 @@ __parser_mark_active(struct i915_vma *vma,
 static int
 parser_mark_active(struct eb_parse_work *pw, struct intel_timeline *tl)
 {
-	int err;
-
-	err = __parser_mark_active(pw->shadow, tl, &pw->base.dma);
-	if (err)
-		return err;
-
-	if (pw->trampoline) {
-		err = __parser_mark_active(pw->trampoline, tl, &pw->base.dma);
-		if (err)
-			return err;
-	}
+	GEM_BUG_ON(pw->trampoline &&
+		   pw->trampoline->private != pw->shadow->private);
 
-	return 0;
+	return __parser_mark_active(pw->shadow, tl, &pw->base.dma);
 }
 
 static int eb_parse_pipeline(struct i915_execbuffer *eb,
 			     struct i915_vma *shadow,
 			     struct i915_vma *trampoline)
 {
+	struct i915_vma *batch = eb->batch->vma;
 	struct eb_parse_work *pw;
+	void *ptr;
 	int err;
 
+	GEM_BUG_ON(!i915_vma_is_pinned(shadow));
+	GEM_BUG_ON(trampoline && !i915_vma_is_pinned(trampoline));
+
 	pw = kzalloc(sizeof(*pw), GFP_KERNEL);
 	if (!pw)
 		return -ENOMEM;
 
+	ptr = i915_gem_object_pin_map(shadow->obj, I915_MAP_FORCE_WB);
+	if (IS_ERR(ptr)) {
+		err = PTR_ERR(ptr);
+		goto err_free;
+	}
+
+	if (!(batch->obj->cache_coherent & I915_BO_CACHE_COHERENT_FOR_READ) &&
+	    i915_has_memcpy_from_wc()) {
+		ptr = i915_gem_object_pin_map(batch->obj, I915_MAP_WC);
+		if (IS_ERR(ptr)) {
+			err = PTR_ERR(ptr);
+			goto err_dst;
+		}
+	} else {
+		__i915_gem_object_pin_pages(batch->obj);
+	}
+
 	err = i915_active_acquire(&eb->batch->vma->active);
 	if (err)
-		goto err_free;
+		goto err_src;
 
 	err = i915_active_acquire(&shadow->active);
 	if (err)
@@ -2620,6 +2751,10 @@ static int eb_parse_pipeline(struct i915_execbuffer *eb,
 	i915_active_release(&shadow->active);
 err_batch:
 	i915_active_release(&eb->batch->vma->active);
+err_src:
+	i915_gem_object_unpin_pages(batch->obj);
+err_dst:
+	i915_gem_object_unpin_pages(shadow->obj);
 err_free:
 	kfree(pw);
 	return err;
@@ -2627,82 +2762,26 @@ static int eb_parse_pipeline(struct i915_execbuffer *eb,
 
 static int eb_parse(struct i915_execbuffer *eb)
 {
-	struct drm_i915_private *i915 = eb->i915;
-	struct intel_gt_buffer_pool_node *pool;
-	struct i915_vma *shadow, *trampoline;
-	unsigned int len;
 	int err;
 
-	if (!eb_use_cmdparser(eb))
-		return 0;
-
-	len = eb->batch_len;
-	if (!CMDPARSER_USES_GGTT(eb->i915)) {
-		/*
-		 * ppGTT backed shadow buffers must be mapped RO, to prevent
-		 * post-scan tampering
-		 */
-		if (!eb->context->vm->has_read_only) {
-			drm_dbg(&i915->drm,
-				"Cannot prevent post-scan tampering without RO capable vm\n");
-			return -EINVAL;
-		}
-	} else {
-		len += I915_CMD_PARSER_TRAMPOLINE_SIZE;
-	}
-
-	pool = intel_gt_get_buffer_pool(eb->engine->gt, len);
-	if (IS_ERR(pool))
-		return PTR_ERR(pool);
-
-	shadow = shadow_batch_pin(pool->obj, eb->context->vm, PIN_USER);
-	if (IS_ERR(shadow)) {
-		err = PTR_ERR(shadow);
-		goto err;
+	if (unlikely(eb->batch->flags & EXEC_OBJECT_WRITE)) {
+		drm_dbg(&eb->i915->drm,
+			"Attempting to use self-modifying batch buffer\n");
+		return -EINVAL;
 	}
-	i915_gem_object_set_readonly(shadow->obj);
-	shadow->private = pool;
-
-	trampoline = NULL;
-	if (CMDPARSER_USES_GGTT(eb->i915)) {
-		trampoline = shadow;
-
-		shadow = shadow_batch_pin(pool->obj,
-					  &eb->engine->gt->ggtt->vm,
-					  PIN_GLOBAL);
-		if (IS_ERR(shadow)) {
-			err = PTR_ERR(shadow);
-			shadow = trampoline;
-			goto err_shadow;
-		}
-		shadow->private = pool;
 
-		eb->batch_flags |= I915_DISPATCH_SECURE;
-	}
+	if (!eb->parser.shadow)
+		return 0;
 
-	err = eb_parse_pipeline(eb, shadow, trampoline);
+	err = eb_parse_pipeline(eb,
+				eb->parser.shadow->vma,
+				eb->parser.trampoline ? eb->parser.trampoline->vma : NULL);
 	if (err)
-		goto err_trampoline;
-
-	eb->batch = &eb->vma[eb->buffer_count++];
-	eb->batch->vma = i915_vma_get(shadow);
-	eb->batch->flags = __EXEC_OBJECT_HAS_PIN;
-	list_add_tail(&eb->batch->submit_link, &eb->submit_list);
-	eb->vma[eb->buffer_count].vma = NULL;
+		return err;
 
-	eb->trampoline = trampoline;
+	eb->batch = eb->parser.shadow;
 	eb->batch_start_offset = 0;
-
 	return 0;
-
-err_trampoline:
-	if (trampoline)
-		i915_vma_unpin(trampoline);
-err_shadow:
-	i915_vma_unpin(shadow);
-err:
-	intel_gt_buffer_pool_put(pool);
-	return err;
 }
 
 static void
@@ -2751,10 +2830,10 @@ static int eb_submit(struct i915_execbuffer *eb, struct i915_vma *batch)
 	if (err)
 		return err;
 
-	if (eb->trampoline) {
+	if (eb->parser.trampoline) {
 		GEM_BUG_ON(eb->batch_start_offset);
 		err = eb->engine->emit_bb_start(eb->request,
-						eb->trampoline->node.start +
+						eb->parser.trampoline->vma->node.start +
 						eb->batch_len,
 						0, 0);
 		if (err)
@@ -3239,7 +3318,7 @@ i915_gem_do_execbuffer(struct drm_device *dev,
 	eb.buffer_count = args->buffer_count;
 	eb.batch_start_offset = args->batch_start_offset;
 	eb.batch_len = args->batch_len;
-	eb.trampoline = NULL;
+	memset(&eb.parser, 0, sizeof(eb.parser));
 
 	eb.batch_flags = 0;
 	if (args->flags & I915_EXEC_SECURE) {
@@ -3305,24 +3384,6 @@ i915_gem_do_execbuffer(struct drm_device *dev,
 		goto err_vma;
 	}
 
-	if (unlikely(eb.batch->flags & EXEC_OBJECT_WRITE)) {
-		drm_dbg(&i915->drm,
-			"Attempting to use self-modifying batch buffer\n");
-		err = -EINVAL;
-		goto err_vma;
-	}
-
-	if (range_overflows_t(u64,
-			      eb.batch_start_offset, eb.batch_len,
-			      eb.batch->vma->size)) {
-		drm_dbg(&i915->drm, "Attempting to use out-of-bounds batch\n");
-		err = -EINVAL;
-		goto err_vma;
-	}
-
-	if (eb.batch_len == 0)
-		eb.batch_len = eb.batch->vma->size - eb.batch_start_offset;
-
 	err = eb_parse(&eb);
 	if (err)
 		goto err_vma;
@@ -3348,7 +3409,7 @@ i915_gem_do_execbuffer(struct drm_device *dev,
 		vma = i915_gem_object_ggtt_pin(batch->obj, NULL, 0, 0, 0);
 		if (IS_ERR(vma)) {
 			err = PTR_ERR(vma);
-			goto err_parse;
+			goto err_vma;
 		}
 
 		GEM_BUG_ON(vma->obj != batch->obj);
@@ -3400,8 +3461,9 @@ i915_gem_do_execbuffer(struct drm_device *dev,
 	 * to explicitly hold another reference here.
 	 */
 	eb.request->batch = batch;
-	if (batch->private)
-		intel_gt_buffer_pool_mark_active(batch->private, eb.request);
+	if (eb.parser.shadow)
+		intel_gt_buffer_pool_mark_active(eb.parser.shadow->vma->private,
+						 eb.request);
 
 	trace_i915_request_queue(eb.request, eb.batch_flags);
 	err = eb_submit(&eb, batch);
@@ -3428,13 +3490,9 @@ i915_gem_do_execbuffer(struct drm_device *dev,
 err_batch_unpin:
 	if (eb.batch_flags & I915_DISPATCH_SECURE)
 		i915_vma_unpin(batch);
-err_parse:
-	if (batch->private)
-		intel_gt_buffer_pool_put(batch->private);
-	i915_vma_put(batch);
 err_vma:
-	if (eb.trampoline)
-		i915_vma_unpin(eb.trampoline);
+	if (eb.parser.shadow)
+		intel_gt_buffer_pool_put(eb.parser.shadow->vma->private);
 	eb_unpin_engine(&eb);
 err_context:
 	i915_gem_context_put(eb.gem_context);
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.h b/drivers/gpu/drm/i915/gem/i915_gem_object.h
index 2faa481cc18f..25714bf70b6a 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_object.h
+++ b/drivers/gpu/drm/i915/gem/i915_gem_object.h
@@ -372,6 +372,16 @@ enum i915_map_type {
 void *__must_check i915_gem_object_pin_map(struct drm_i915_gem_object *obj,
 					   enum i915_map_type type);
 
+static inline void *__i915_gem_object_mapping(struct drm_i915_gem_object *obj)
+{
+	return page_mask_bits(obj->mm.mapping);
+}
+
+static inline int __i915_gem_object_mapping_type(struct drm_i915_gem_object *obj)
+{
+	return page_unmask_bits(obj->mm.mapping);
+}
+
 void __i915_gem_object_flush_map(struct drm_i915_gem_object *obj,
 				 unsigned long offset,
 				 unsigned long size);
diff --git a/drivers/gpu/drm/i915/i915_cmd_parser.c b/drivers/gpu/drm/i915/i915_cmd_parser.c
index 372354d33f55..dc8770206bb8 100644
--- a/drivers/gpu/drm/i915/i915_cmd_parser.c
+++ b/drivers/gpu/drm/i915/i915_cmd_parser.c
@@ -1140,29 +1140,22 @@ static u32 *copy_batch(struct drm_i915_gem_object *dst_obj,
 {
 	bool needs_clflush;
 	void *dst, *src;
-	int ret;
 
-	dst = i915_gem_object_pin_map(dst_obj, I915_MAP_FORCE_WB);
-	if (IS_ERR(dst))
-		return dst;
+	GEM_BUG_ON(!i915_gem_object_has_pages(src_obj));
 
-	ret = i915_gem_object_pin_pages(src_obj);
-	if (ret) {
-		i915_gem_object_unpin_map(dst_obj);
-		return ERR_PTR(ret);
-	}
+	dst = __i915_gem_object_mapping(dst_obj);
+	GEM_BUG_ON(!dst);
 
 	needs_clflush =
 		!(src_obj->cache_coherent & I915_BO_CACHE_COHERENT_FOR_READ);
 
 	src = ERR_PTR(-ENODEV);
 	if (needs_clflush && i915_has_memcpy_from_wc()) {
-		src = i915_gem_object_pin_map(src_obj, I915_MAP_WC);
-		if (!IS_ERR(src)) {
+		if (__i915_gem_object_mapping_type(src_obj) == I915_MAP_WC) {
+			src = __i915_gem_object_mapping(src_obj);
 			i915_unaligned_memcpy_from_wc(dst,
 						      src + offset,
 						      length);
-			i915_gem_object_unpin_map(src_obj);
 		}
 	}
 	if (IS_ERR(src)) {
@@ -1198,9 +1191,6 @@ static u32 *copy_batch(struct drm_i915_gem_object *dst_obj,
 		}
 	}
 
-	i915_gem_object_unpin_pages(src_obj);
-
-	/* dst_obj is returned with vmap pinned */
 	return dst;
 }
 
@@ -1546,7 +1536,6 @@ int intel_engine_cmd_parser(struct intel_engine_cs *engine,
 
 	if (!IS_ERR_OR_NULL(jump_whitelist))
 		kfree(jump_whitelist);
-	i915_gem_object_unpin_map(shadow->obj);
 	return ret;
 }
 
-- 
2.20.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [Intel-gfx] [PATCH 15/20] drm/i915/gem: Include secure batch in common execbuf pinning
  2020-07-06  6:19 [Intel-gfx] s/obj->mm.lock// Chris Wilson
                   ` (13 preceding siblings ...)
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 14/20] drm/i915/gem: Include cmdparser in common execbuf pinning Chris Wilson
@ 2020-07-06  6:19 ` Chris Wilson
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 16/20] drm/i915/gem: Reintroduce multiple passes for reloc processing Chris Wilson
                   ` (9 subsequent siblings)
  24 siblings, 0 replies; 65+ messages in thread
From: Chris Wilson @ 2020-07-06  6:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: Chris Wilson

Pull the GGTT binding for the secure batch dispatch into the common vma
pinning routine for execbuf, so that there is just a single central
place for all i915_vma_pin().

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 .../gpu/drm/i915/gem/i915_gem_execbuffer.c    | 88 +++++++++++--------
 1 file changed, 51 insertions(+), 37 deletions(-)

diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
index 8e4681427ce3..629b736adf2c 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
@@ -1648,6 +1648,48 @@ static int eb_alloc_cmdparser(struct i915_execbuffer *eb)
 	return err;
 }
 
+static int eb_secure_batch(struct i915_execbuffer *eb)
+{
+	struct i915_vma *vma = eb->batch->vma;
+
+	/*
+	 * snb/ivb/vlv conflate the "batch in ppgtt" bit with the "non-secure
+	 * batch" bit. Hence we need to pin secure batches into the global gtt.
+	 * hsw should have this fixed, but bdw mucks it up again.
+	 */
+	if (!(eb->batch_flags & I915_DISPATCH_SECURE))
+		return 0;
+
+	if (GEM_WARN_ON(vma->vm != &eb->engine->gt->ggtt->vm)) {
+		struct eb_vma *ev;
+
+		ev = kzalloc(sizeof(*ev), GFP_KERNEL);
+		if (!ev)
+			return -ENOMEM;
+
+		vma = i915_vma_instance(vma->obj,
+					&eb->engine->gt->ggtt->vm,
+					NULL);
+		if (IS_ERR(vma)) {
+			kfree(ev);
+			return PTR_ERR(vma);
+		}
+
+		ev->vma = i915_vma_get(vma);
+		ev->exec = &no_entry;
+
+		list_add(&ev->submit_link, &eb->submit_list);
+		list_add(&ev->reloc_link, &eb->array->aux_list);
+		list_add(&ev->bind_link, &eb->bind_list);
+
+		GEM_BUG_ON(eb->batch->vma->private);
+		eb->batch = ev;
+	}
+
+	eb->batch->flags |= EXEC_OBJECT_NEEDS_GTT;
+	return 0;
+}
+
 static unsigned int eb_batch_index(const struct i915_execbuffer *eb)
 {
 	if (eb->args->flags & I915_EXEC_BATCH_FIRST)
@@ -2442,6 +2484,10 @@ static int eb_relocate(struct i915_execbuffer *eb)
 	if (err)
 		return err;
 
+	err = eb_secure_batch(eb);
+	if (err)
+		return err;
+
 	err = eb_reserve_vm(eb);
 	if (err)
 		return err;
@@ -2796,7 +2842,7 @@ add_to_client(struct i915_request *rq, struct drm_file *file)
 	spin_unlock(&file_priv->mm.lock);
 }
 
-static int eb_submit(struct i915_execbuffer *eb, struct i915_vma *batch)
+static int eb_submit(struct i915_execbuffer *eb)
 {
 	int err;
 
@@ -2823,7 +2869,7 @@ static int eb_submit(struct i915_execbuffer *eb, struct i915_vma *batch)
 	}
 
 	err = eb->engine->emit_bb_start(eb->request,
-					batch->node.start +
+					eb->batch->vma->node.start +
 					eb->batch_start_offset,
 					eb->batch_len,
 					eb->batch_flags);
@@ -3296,7 +3342,6 @@ i915_gem_do_execbuffer(struct drm_device *dev,
 	struct i915_execbuffer eb;
 	struct dma_fence *in_fence = NULL;
 	struct sync_file *out_fence = NULL;
-	struct i915_vma *batch;
 	int out_fence_fd = -1;
 	int err;
 
@@ -3388,34 +3433,6 @@ i915_gem_do_execbuffer(struct drm_device *dev,
 	if (err)
 		goto err_vma;
 
-	/*
-	 * snb/ivb/vlv conflate the "batch in ppgtt" bit with the "non-secure
-	 * batch" bit. Hence we need to pin secure batches into the global gtt.
-	 * hsw should have this fixed, but bdw mucks it up again. */
-	batch = i915_vma_get(eb.batch->vma);
-	if (eb.batch_flags & I915_DISPATCH_SECURE) {
-		struct i915_vma *vma;
-
-		/*
-		 * So on first glance it looks freaky that we pin the batch here
-		 * outside of the reservation loop. But:
-		 * - The batch is already pinned into the relevant ppgtt, so we
-		 *   already have the backing storage fully allocated.
-		 * - No other BO uses the global gtt (well contexts, but meh),
-		 *   so we don't really have issues with multiple objects not
-		 *   fitting due to fragmentation.
-		 * So this is actually safe.
-		 */
-		vma = i915_gem_object_ggtt_pin(batch->obj, NULL, 0, 0, 0);
-		if (IS_ERR(vma)) {
-			err = PTR_ERR(vma);
-			goto err_vma;
-		}
-
-		GEM_BUG_ON(vma->obj != batch->obj);
-		batch = vma;
-	}
-
 	/* All GPU relocation batches must be submitted prior to the user rq */
 	GEM_BUG_ON(eb.reloc_cache.rq);
 
@@ -3423,7 +3440,7 @@ i915_gem_do_execbuffer(struct drm_device *dev,
 	eb.request = __i915_request_create(eb.context, GFP_KERNEL);
 	if (IS_ERR(eb.request)) {
 		err = PTR_ERR(eb.request);
-		goto err_batch_unpin;
+		goto err_vma;
 	}
 	eb.request->cookie = lockdep_pin_lock(&eb.context->timeline->mutex);
 
@@ -3460,13 +3477,13 @@ i915_gem_do_execbuffer(struct drm_device *dev,
 	 * inactive_list and lose its active reference. Hence we do not need
 	 * to explicitly hold another reference here.
 	 */
-	eb.request->batch = batch;
+	eb.request->batch = eb.batch->vma;
 	if (eb.parser.shadow)
 		intel_gt_buffer_pool_mark_active(eb.parser.shadow->vma->private,
 						 eb.request);
 
 	trace_i915_request_queue(eb.request, eb.batch_flags);
-	err = eb_submit(&eb, batch);
+	err = eb_submit(&eb);
 err_request:
 	add_to_client(eb.request, file);
 	i915_request_get(eb.request);
@@ -3487,9 +3504,6 @@ i915_gem_do_execbuffer(struct drm_device *dev,
 	}
 	i915_request_put(eb.request);
 
-err_batch_unpin:
-	if (eb.batch_flags & I915_DISPATCH_SECURE)
-		i915_vma_unpin(batch);
 err_vma:
 	if (eb.parser.shadow)
 		intel_gt_buffer_pool_put(eb.parser.shadow->vma->private);
-- 
2.20.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [Intel-gfx] [PATCH 16/20] drm/i915/gem: Reintroduce multiple passes for reloc processing
  2020-07-06  6:19 [Intel-gfx] s/obj->mm.lock// Chris Wilson
                   ` (14 preceding siblings ...)
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 15/20] drm/i915/gem: Include secure batch " Chris Wilson
@ 2020-07-06  6:19 ` Chris Wilson
  2020-07-09 15:39   ` Tvrtko Ursulin
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 17/20] drm/i915: Add an implementation for i915_gem_ww_ctx locking, v2 Chris Wilson
                   ` (8 subsequent siblings)
  24 siblings, 1 reply; 65+ messages in thread
From: Chris Wilson @ 2020-07-06  6:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: Chris Wilson

The prospect of locking the entire submission sequence under a wide
ww_mutex re-imposes some key restrictions, in particular that we must
not call copy_(from|to)_user underneath the mutex (as the faulthandlers
themselves may need to take the ww_mutex). To satisfy this requirement,
we need to split the relocation handling into multiple phases again.
After dropping the reservations, we need to allocate enough buffer space
to both copy the relocations from userspace into, and serve as the
relocation command buffer. Once we have finished copying the
relocations, we can then re-aquire all the objects for the execbuf and
rebind them, including our new relocations objects. After we have bound
all the new and old objects into their final locations, we can then
convert the relocation entries into the GPU commands to update the
relocated vma. Finally, once it is all over and we have dropped the
ww_mutex for the last time, we can then complete the update of the user
relocation entries.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 .../gpu/drm/i915/gem/i915_gem_execbuffer.c    | 842 ++++++++----------
 .../i915/gem/selftests/i915_gem_execbuffer.c  | 195 ++--
 2 files changed, 520 insertions(+), 517 deletions(-)

diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
index 629b736adf2c..fbf5c5cd51ca 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
@@ -35,6 +35,7 @@ struct eb_vma {
 
 	/** This vma's place in the execbuf reservation list */
 	struct drm_i915_gem_exec_object2 *exec;
+	u32 bias;
 
 	struct list_head bind_link;
 	struct list_head unbound_link;
@@ -60,15 +61,12 @@ struct eb_vma_array {
 #define __EXEC_OBJECT_HAS_PIN		BIT(31)
 #define __EXEC_OBJECT_HAS_FENCE		BIT(30)
 #define __EXEC_OBJECT_NEEDS_MAP		BIT(29)
-#define __EXEC_OBJECT_NEEDS_BIAS	BIT(28)
-#define __EXEC_OBJECT_INTERNAL_FLAGS	(~0u << 28) /* all of the above */
+#define __EXEC_OBJECT_INTERNAL_FLAGS	(~0u << 29) /* all of the above */
 
 #define __EXEC_HAS_RELOC	BIT(31)
 #define __EXEC_INTERNAL_FLAGS	(~0u << 31)
 #define UPDATE			PIN_OFFSET_FIXED
 
-#define BATCH_OFFSET_BIAS (256*1024)
-
 #define __I915_EXEC_ILLEGAL_FLAGS \
 	(__I915_EXEC_UNKNOWN_FLAGS | \
 	 I915_EXEC_CONSTANTS_MASK  | \
@@ -266,20 +264,18 @@ struct i915_execbuffer {
 	 * obj/page
 	 */
 	struct reloc_cache {
-		struct drm_mm_node node; /** temporary GTT binding */
 		unsigned int gen; /** Cached value of INTEL_GEN */
 		bool use_64bit_reloc : 1;
-		bool has_llc : 1;
 		bool has_fence : 1;
 		bool needs_unfenced : 1;
 
 		struct intel_context *ce;
 
-		struct i915_vma *target;
-		struct i915_request *rq;
-		struct i915_vma *rq_vma;
-		u32 *rq_cmd;
-		unsigned int rq_size;
+		struct eb_relocs_link {
+			struct i915_vma *vma;
+		} head;
+		struct drm_i915_gem_relocation_entry *map;
+		unsigned int pos;
 	} reloc_cache;
 
 	struct eb_cmdparser {
@@ -288,7 +284,7 @@ struct i915_execbuffer {
 	} parser;
 
 	u64 invalid_flags; /** Set of execobj.flags that are invalid */
-	u32 context_flags; /** Set of execobj.flags to insert from the ctx */
+	u32 context_bias;
 
 	u32 batch_start_offset; /** Location within object of batch */
 	u32 batch_len; /** Length of batch within object */
@@ -308,6 +304,12 @@ static struct drm_i915_gem_exec_object2 no_entry = {
 	.offset = -1ull
 };
 
+static u64 noncanonical_addr(u64 addr, const struct i915_address_space *vm)
+{
+	GEM_BUG_ON(!is_power_of_2(vm->total));
+	return addr & (vm->total - 1);
+}
+
 static inline bool eb_use_cmdparser(const struct i915_execbuffer *eb)
 {
 	return intel_engine_requires_cmd_parser(eb->engine) ||
@@ -479,11 +481,12 @@ static int eb_create(struct i915_execbuffer *eb)
 	return 0;
 }
 
-static bool
-eb_vma_misplaced(const struct drm_i915_gem_exec_object2 *entry,
-		 const struct i915_vma *vma,
-		 unsigned int flags)
+static bool eb_vma_misplaced(const struct eb_vma *ev)
 {
+	const struct drm_i915_gem_exec_object2 *entry = ev->exec;
+	const struct i915_vma *vma = ev->vma;
+	unsigned int flags = ev->flags;
+
 	if (test_bit(I915_VMA_ERROR_BIT, __i915_vma_flags(vma)))
 		return true;
 
@@ -497,8 +500,7 @@ eb_vma_misplaced(const struct drm_i915_gem_exec_object2 *entry,
 	    vma->node.start != entry->offset)
 		return true;
 
-	if (flags & __EXEC_OBJECT_NEEDS_BIAS &&
-	    vma->node.start < BATCH_OFFSET_BIAS)
+	if (vma->node.start < ev->bias)
 		return true;
 
 	if (!(flags & EXEC_OBJECT_SUPPORTS_48B_ADDRESS) &&
@@ -543,7 +545,7 @@ eb_pin_vma_inplace(struct i915_execbuffer *eb,
 	if (!i915_active_is_idle(&vma->vm->binding))
 		return false;
 
-	if (eb_vma_misplaced(entry, vma, ev->flags))
+	if (eb_vma_misplaced(ev))
 		return false;
 
 	pin_flags = PIN_USER;
@@ -561,7 +563,7 @@ eb_pin_vma_inplace(struct i915_execbuffer *eb,
 		}
 	}
 
-	GEM_BUG_ON(eb_vma_misplaced(entry, vma, ev->flags));
+	GEM_BUG_ON(eb_vma_misplaced(ev));
 
 	ev->flags |= __EXEC_OBJECT_HAS_PIN;
 	return true;
@@ -599,7 +601,7 @@ eb_validate_vma(struct i915_execbuffer *eb,
 	 * so from this point we're always using non-canonical
 	 * form internally.
 	 */
-	entry->offset = gen8_noncanonical_addr(entry->offset);
+	entry->offset = noncanonical_addr(entry->offset, eb->context->vm);
 
 	if (!eb->reloc_cache.has_fence) {
 		entry->flags &= ~EXEC_OBJECT_NEEDS_FENCE;
@@ -610,9 +612,6 @@ eb_validate_vma(struct i915_execbuffer *eb,
 			entry->flags |= EXEC_OBJECT_NEEDS_GTT | __EXEC_OBJECT_NEEDS_MAP;
 	}
 
-	if (!(entry->flags & EXEC_OBJECT_PINNED))
-		entry->flags |= eb->context_flags;
-
 	return 0;
 }
 
@@ -629,7 +628,9 @@ eb_add_vma(struct i915_execbuffer *eb,
 	ev->vma = vma;
 	ev->exec = entry;
 	ev->flags = entry->flags;
+	ev->bias = eb->context_bias;
 
+	ev->handle = entry->handle;
 	if (eb->lut_size > 0) {
 		ev->handle = entry->handle;
 		hlist_add_head(&ev->node,
@@ -655,7 +656,8 @@ eb_add_vma(struct i915_execbuffer *eb,
 	if (i == batch_idx) {
 		if (entry->relocation_count &&
 		    !(ev->flags & EXEC_OBJECT_PINNED))
-			ev->flags |= __EXEC_OBJECT_NEEDS_BIAS;
+			ev->bias = max_t(u32, ev->bias, SZ_256K);
+
 		if (eb->reloc_cache.has_fence)
 			ev->flags |= EXEC_OBJECT_NEEDS_FENCE;
 
@@ -981,7 +983,8 @@ static int eb_reserve_vma(struct eb_vm_work *work, struct eb_bind_vma *bind)
 	const unsigned int exec_flags = bind->ev->flags;
 	struct i915_vma *vma = bind->ev->vma;
 	struct i915_address_space *vm = vma->vm;
-	u64 start = 0, end = vm->total;
+	u64 start = round_up(bind->ev->bias, I915_GTT_MIN_ALIGNMENT);
+	u64 end = vm->total;
 	u64 align = entry->alignment ?: I915_GTT_MIN_ALIGNMENT;
 	unsigned int bind_flags;
 	int err;
@@ -1001,7 +1004,7 @@ static int eb_reserve_vma(struct eb_vm_work *work, struct eb_bind_vma *bind)
 	GEM_BUG_ON(!vma->size);
 
 	/* Reuse old address (if it doesn't conflict with new requirements) */
-	if (eb_vma_misplaced(entry, vma, exec_flags)) {
+	if (eb_vma_misplaced(bind->ev)) {
 		vma->node.start = entry->offset & PIN_OFFSET_MASK;
 		vma->node.size = max(entry->pad_to_size, vma->size);
 		vma->node.color = 0;
@@ -1023,11 +1026,8 @@ static int eb_reserve_vma(struct eb_vm_work *work, struct eb_bind_vma *bind)
 		align = max_t(u64, align, vma->fence_alignment);
 	}
 
-	if (exec_flags & __EXEC_OBJECT_NEEDS_BIAS)
-		start = BATCH_OFFSET_BIAS;
-
 	GEM_BUG_ON(!vma->node.size);
-	if (vma->node.size > end - start)
+	if (start > end || vma->node.size > end - start)
 		return -E2BIG;
 
 	/* Try the user's preferred location first (mandatory if soft-pinned) */
@@ -1110,7 +1110,7 @@ static int eb_reserve_vma(struct eb_vm_work *work, struct eb_bind_vma *bind)
 	}
 
 	bind->ev->flags |= __EXEC_OBJECT_HAS_PIN;
-	GEM_BUG_ON(eb_vma_misplaced(entry, vma, bind->ev->flags));
+	GEM_BUG_ON(eb_vma_misplaced(bind->ev));
 
 	if (unlikely(exec_flags & EXEC_OBJECT_NEEDS_FENCE)) {
 		err = __i915_vma_pin_fence_async(vma, &work->base);
@@ -1346,8 +1346,7 @@ static int wait_for_unbinds(struct i915_execbuffer *eb,
 
 		GEM_BUG_ON(ev->flags & __EXEC_OBJECT_HAS_PIN);
 
-		if (drm_mm_node_allocated(&vma->node) &&
-		    eb_vma_misplaced(ev->exec, vma, ev->flags)) {
+		if (drm_mm_node_allocated(&vma->node) && eb_vma_misplaced(ev)) {
 			err = i915_vma_unbind(vma);
 			if (err)
 				return err;
@@ -1467,7 +1466,7 @@ static int eb_reserve_vm(struct i915_execbuffer *eb)
 			 * we cannot handle migrating the vma inside the worker.
 			 */
 			if (drm_mm_node_allocated(&vma->node)) {
-				if (eb_vma_misplaced(ev->exec, vma, ev->flags)) {
+				if (eb_vma_misplaced(ev)) {
 					err = -ENOSPC;
 					break;
 				}
@@ -1710,9 +1709,9 @@ static int eb_select_context(struct i915_execbuffer *eb)
 	if (rcu_access_pointer(ctx->vm))
 		eb->invalid_flags |= EXEC_OBJECT_NEEDS_GTT;
 
-	eb->context_flags = 0;
+	eb->context_bias = 0;
 	if (test_bit(UCONTEXT_NO_ZEROMAP, &ctx->user_flags))
-		eb->context_flags |= __EXEC_OBJECT_NEEDS_BIAS;
+		eb->context_bias = I915_GTT_MIN_ALIGNMENT;
 
 	return 0;
 }
@@ -1858,8 +1857,6 @@ eb_get_vma(const struct i915_execbuffer *eb, unsigned long handle)
 
 static void eb_destroy(const struct i915_execbuffer *eb)
 {
-	GEM_BUG_ON(eb->reloc_cache.rq);
-
 	eb_vma_array_put(eb->array);
 	if (eb->lut_size > 0)
 		kfree(eb->buckets);
@@ -1877,98 +1874,25 @@ static void reloc_cache_init(struct reloc_cache *cache,
 {
 	/* Must be a variable in the struct to allow GCC to unroll. */
 	cache->gen = INTEL_GEN(i915);
-	cache->has_llc = HAS_LLC(i915);
 	cache->use_64bit_reloc = HAS_64BIT_RELOC(i915);
 	cache->has_fence = cache->gen < 4;
 	cache->needs_unfenced = INTEL_INFO(i915)->unfenced_needs_alignment;
-	cache->node.flags = 0;
-	cache->rq = NULL;
-	cache->target = NULL;
-}
-
-#define RELOC_TAIL 4
-
-static int reloc_gpu_chain(struct reloc_cache *cache)
-{
-	struct intel_gt_buffer_pool_node *pool;
-	struct i915_request *rq = cache->rq;
-	struct i915_vma *batch;
-	u32 *cmd;
-	int err;
-
-	pool = intel_gt_get_buffer_pool(rq->engine->gt, PAGE_SIZE);
-	if (IS_ERR(pool))
-		return PTR_ERR(pool);
-
-	batch = i915_vma_instance(pool->obj, rq->context->vm, NULL);
-	if (IS_ERR(batch)) {
-		err = PTR_ERR(batch);
-		goto out_pool;
-	}
-
-	err = i915_vma_pin(batch, 0, 0, PIN_USER | PIN_NONBLOCK);
-	if (err)
-		goto out_pool;
-
-	GEM_BUG_ON(cache->rq_size + RELOC_TAIL > PAGE_SIZE  / sizeof(u32));
-	cmd = cache->rq_cmd + cache->rq_size;
-	*cmd++ = MI_ARB_CHECK;
-	if (cache->gen >= 8)
-		*cmd++ = MI_BATCH_BUFFER_START_GEN8;
-	else if (cache->gen >= 6)
-		*cmd++ = MI_BATCH_BUFFER_START;
-	else
-		*cmd++ = MI_BATCH_BUFFER_START | MI_BATCH_GTT;
-	*cmd++ = lower_32_bits(batch->node.start);
-	*cmd++ = upper_32_bits(batch->node.start); /* Always 0 for gen<8 */
-	i915_gem_object_flush_map(cache->rq_vma->obj);
-	i915_gem_object_unpin_map(cache->rq_vma->obj);
-	cache->rq_vma = NULL;
-
-	err = intel_gt_buffer_pool_mark_active(pool, rq);
-	if (err == 0) {
-		i915_vma_lock(batch);
-		err = i915_request_await_object(rq, batch->obj, false);
-		if (err == 0)
-			err = i915_vma_move_to_active(batch, rq, 0);
-		i915_vma_unlock(batch);
-	}
-	i915_vma_unpin(batch);
-	if (err)
-		goto out_pool;
-
-	cmd = i915_gem_object_pin_map(batch->obj,
-				      cache->has_llc ?
-				      I915_MAP_FORCE_WB :
-				      I915_MAP_FORCE_WC);
-	if (IS_ERR(cmd)) {
-		err = PTR_ERR(cmd);
-		goto out_pool;
-	}
-
-	/* Return with batch mapping (cmd) still pinned */
-	cache->rq_cmd = cmd;
-	cache->rq_size = 0;
-	cache->rq_vma = batch;
-
-out_pool:
-	intel_gt_buffer_pool_put(pool);
-	return err;
 }
 
 static struct i915_request *
-nested_request_create(struct intel_context *ce)
+nested_request_create(struct intel_context *ce, struct i915_execbuffer *eb)
 {
 	struct i915_request *rq;
 
 	/* XXX This only works once; replace with shared timeline */
-	mutex_lock_nested(&ce->timeline->mutex, SINGLE_DEPTH_NESTING);
+	if (ce->timeline != eb->context->timeline)
+		mutex_lock_nested(&ce->timeline->mutex, SINGLE_DEPTH_NESTING);
 	intel_context_enter(ce);
 
 	rq = __i915_request_create(ce, GFP_KERNEL);
 
 	intel_context_exit(ce);
-	if (IS_ERR(rq))
+	if (IS_ERR(rq) && ce->timeline != eb->context->timeline)
 		mutex_unlock(&ce->timeline->mutex);
 
 	return rq;
@@ -1991,28 +1915,18 @@ static unsigned int reloc_bb_flags(const struct reloc_cache *cache)
 	return cache->gen > 5 ? 0 : I915_DISPATCH_SECURE;
 }
 
-static int reloc_gpu_flush(struct i915_execbuffer *eb)
+static int
+reloc_gpu_flush(struct i915_execbuffer *eb, struct i915_request *rq, int err)
 {
 	struct reloc_cache *cache = &eb->reloc_cache;
-	struct i915_request *rq;
-	int err;
-
-	rq = fetch_and_zero(&cache->rq);
-	if (!rq)
-		return 0;
-
-	if (cache->rq_vma) {
-		struct drm_i915_gem_object *obj = cache->rq_vma->obj;
-
-		GEM_BUG_ON(cache->rq_size >= obj->base.size / sizeof(u32));
-		cache->rq_cmd[cache->rq_size++] = MI_BATCH_BUFFER_END;
+	u32 *cs;
 
-		__i915_gem_object_flush_map(obj,
-					    0, sizeof(u32) * cache->rq_size);
-		i915_gem_object_unpin_map(obj);
-	}
+	cs = (u32 *)(cache->map + cache->pos);
+	*cs++ = MI_BATCH_BUFFER_END;
+	__i915_gem_object_flush_map(cache->head.vma->obj,
+				    0, (void *)cs - (void *)cache->map);
+	i915_gem_object_unpin_map(cache->head.vma->obj);
 
-	err = 0;
 	if (rq->engine->emit_init_breadcrumb)
 		err = rq->engine->emit_init_breadcrumb(rq);
 	if (!err)
@@ -2025,6 +1939,7 @@ static int reloc_gpu_flush(struct i915_execbuffer *eb)
 
 	intel_gt_chipset_flush(rq->engine->gt);
 	__i915_request_add(rq, &eb->gem_context->sched);
+
 	if (i915_request_timeline(rq) != eb->context->timeline)
 		mutex_unlock(&i915_request_timeline(rq)->mutex);
 
@@ -2042,7 +1957,7 @@ static int reloc_move_to_gpu(struct i915_request *rq, struct i915_vma *vma)
 		i915_gem_clflush_object(obj, 0);
 	obj->write_domain = 0;
 
-	err = i915_request_await_object(rq, vma->obj, true);
+	err = i915_request_await_object(rq, obj, true);
 	if (err == 0)
 		err = i915_vma_move_to_active(vma, rq, EXEC_OBJECT_WRITE);
 
@@ -2051,130 +1966,6 @@ static int reloc_move_to_gpu(struct i915_request *rq, struct i915_vma *vma)
 	return err;
 }
 
-static int __reloc_gpu_alloc(struct i915_execbuffer *eb,
-			     struct intel_engine_cs *engine,
-			     unsigned int len)
-{
-	struct reloc_cache *cache = &eb->reloc_cache;
-	struct intel_gt_buffer_pool_node *pool;
-	struct i915_request *rq;
-	struct i915_vma *batch;
-	u32 *cmd;
-	int err;
-
-	pool = intel_gt_get_buffer_pool(engine->gt, PAGE_SIZE);
-	if (IS_ERR(pool))
-		return PTR_ERR(pool);
-
-	cmd = i915_gem_object_pin_map(pool->obj,
-				      cache->has_llc ?
-				      I915_MAP_FORCE_WB :
-				      I915_MAP_FORCE_WC);
-	if (IS_ERR(cmd)) {
-		err = PTR_ERR(cmd);
-		goto out_pool;
-	}
-
-	batch = i915_vma_instance(pool->obj, eb->context->vm, NULL);
-	if (IS_ERR(batch)) {
-		err = PTR_ERR(batch);
-		goto err_unmap;
-	}
-
-	err = i915_vma_pin(batch, 0, 0, PIN_USER | PIN_NONBLOCK);
-	if (err)
-		goto err_unmap;
-
-	if (cache->ce == eb->context)
-		rq = __i915_request_create(cache->ce, GFP_KERNEL);
-	else
-		rq = nested_request_create(cache->ce);
-	if (IS_ERR(rq)) {
-		err = PTR_ERR(rq);
-		goto err_unpin;
-	}
-	rq->cookie = lockdep_pin_lock(&i915_request_timeline(rq)->mutex);
-
-	err = intel_gt_buffer_pool_mark_active(pool, rq);
-	if (err)
-		goto err_request;
-
-	i915_vma_lock(batch);
-	err = i915_request_await_object(rq, batch->obj, false);
-	if (err == 0)
-		err = i915_vma_move_to_active(batch, rq, 0);
-	i915_vma_unlock(batch);
-	if (err)
-		goto skip_request;
-
-	rq->batch = batch;
-	i915_vma_unpin(batch);
-
-	cache->rq = rq;
-	cache->rq_cmd = cmd;
-	cache->rq_size = 0;
-	cache->rq_vma = batch;
-
-	/* Return with batch mapping (cmd) still pinned */
-	goto out_pool;
-
-skip_request:
-	i915_request_set_error_once(rq, err);
-err_request:
-	__i915_request_add(rq, &eb->gem_context->sched);
-	if (i915_request_timeline(rq) != eb->context->timeline)
-		mutex_unlock(&i915_request_timeline(rq)->mutex);
-err_unpin:
-	i915_vma_unpin(batch);
-err_unmap:
-	i915_gem_object_unpin_map(pool->obj);
-out_pool:
-	intel_gt_buffer_pool_put(pool);
-	return err;
-}
-
-static u32 *reloc_gpu(struct i915_execbuffer *eb,
-		      struct i915_vma *vma,
-		      unsigned int len)
-{
-	struct reloc_cache *cache = &eb->reloc_cache;
-	u32 *cmd;
-	int err;
-
-	if (unlikely(!cache->rq)) {
-		struct intel_engine_cs *engine = eb->engine;
-
-		err = __reloc_gpu_alloc(eb, engine, len);
-		if (unlikely(err))
-			return ERR_PTR(err);
-	}
-
-	if (vma != cache->target) {
-		err = reloc_move_to_gpu(cache->rq, vma);
-		if (unlikely(err)) {
-			i915_request_set_error_once(cache->rq, err);
-			return ERR_PTR(err);
-		}
-
-		cache->target = vma;
-	}
-
-	if (unlikely(cache->rq_size + len >
-		     PAGE_SIZE / sizeof(u32) - RELOC_TAIL)) {
-		err = reloc_gpu_chain(cache);
-		if (unlikely(err)) {
-			i915_request_set_error_once(cache->rq, err);
-			return ERR_PTR(err);
-		}
-	}
-
-	GEM_BUG_ON(cache->rq_size + len >= PAGE_SIZE  / sizeof(u32));
-	cmd = cache->rq_cmd + cache->rq_size;
-	cache->rq_size += len;
-
-	return cmd;
-}
-
 static unsigned long vma_phys_addr(struct i915_vma *vma, u32 offset)
 {
 	struct page *page;
@@ -2189,30 +1980,30 @@ static unsigned long vma_phys_addr(struct i915_vma *vma, u32 offset)
 	return addr + offset_in_page(offset);
 }
 
-static int __reloc_entry_gpu(struct i915_execbuffer *eb,
-			     struct i915_vma *vma,
-			     u64 offset,
-			     u64 target_addr)
+static bool
+eb_relocs_vma_entry(struct i915_execbuffer *eb,
+		    const struct eb_vma *ev,
+		    struct drm_i915_gem_relocation_entry *reloc)
 {
 	const unsigned int gen = eb->reloc_cache.gen;
-	unsigned int len;
+	struct i915_vma *target = eb_get_vma(eb, reloc->target_handle)->vma;
+	const u64 target_addr = relocation_target(reloc, target);
+	const u64 presumed =
+		noncanonical_addr(reloc->presumed_offset, target->vm);
+	u64 offset = reloc->offset;
 	u32 *batch;
-	u64 addr;
 
-	if (gen >= 8)
-		len = offset & 7 ? 8 : 5;
-	else if (gen >= 4)
-		len = 4;
-	else
-		len = 3;
+	GEM_BUG_ON(!i915_vma_is_pinned(target));
 
-	batch = reloc_gpu(eb, vma, len);
-	if (IS_ERR(batch))
-		return PTR_ERR(batch);
+	/* Replace the reloc entry with the GPU commands */
+	batch = memset(reloc, 0, sizeof(*reloc));
+	if (presumed == target->node.start)
+		return false;
 
-	addr = gen8_canonical_addr(vma->node.start + offset);
 	if (gen >= 8) {
-		if (offset & 7) {
+		u64 addr = gen8_canonical_addr(ev->vma->node.start + offset);
+
+		if (addr & 7) {
 			*batch++ = MI_STORE_DWORD_IMM_GEN4;
 			*batch++ = lower_32_bits(addr);
 			*batch++ = upper_32_bits(addr);
@@ -2234,107 +2025,65 @@ static int __reloc_entry_gpu(struct i915_execbuffer *eb,
 	} else if (gen >= 6) {
 		*batch++ = MI_STORE_DWORD_IMM_GEN4;
 		*batch++ = 0;
-		*batch++ = addr;
+		*batch++ = ev->vma->node.start + offset;
 		*batch++ = target_addr;
 	} else if (IS_I965G(eb->i915)) {
 		*batch++ = MI_STORE_DWORD_IMM_GEN4;
 		*batch++ = 0;
-		*batch++ = vma_phys_addr(vma, offset);
+		*batch++ = vma_phys_addr(ev->vma, offset);
 		*batch++ = target_addr;
 	} else if (gen >= 4) {
 		*batch++ = MI_STORE_DWORD_IMM_GEN4 | MI_USE_GGTT;
 		*batch++ = 0;
-		*batch++ = addr;
+		*batch++ = ev->vma->node.start + offset;
 		*batch++ = target_addr;
-	} else if (gen >= 3 &&
-		   !(IS_I915G(eb->i915) || IS_I915GM(eb->i915))) {
+	} else if (gen >= 3 && !(IS_I915G(eb->i915) || IS_I915GM(eb->i915))) {
 		*batch++ = MI_STORE_DWORD_IMM | MI_MEM_VIRTUAL;
-		*batch++ = addr;
+		*batch++ = ev->vma->node.start + offset;
 		*batch++ = target_addr;
 	} else {
 		*batch++ = MI_STORE_DWORD_IMM;
-		*batch++ = vma_phys_addr(vma, offset);
+		*batch++ = vma_phys_addr(ev->vma, offset);
 		*batch++ = target_addr;
 	}
+	GEM_BUG_ON(batch > (u32 *)(reloc + 1));
 
-	return 0;
-}
-
-static u64
-relocate_entry(struct i915_execbuffer *eb,
-	       struct i915_vma *vma,
-	       const struct drm_i915_gem_relocation_entry *reloc,
-	       const struct i915_vma *target)
-{
-	u64 target_addr = relocation_target(reloc, target);
-	int err;
-
-	err = __reloc_entry_gpu(eb, vma, reloc->offset, target_addr);
-	if (err)
-		return err;
-
-	return target->node.start | UPDATE;
-}
-
-static int gen6_fixup_ggtt(struct i915_vma *vma)
-{
-	int err;
-
-	if (i915_vma_is_bound(vma, I915_VMA_GLOBAL_BIND))
-		return 0;
-
-	err = i915_vma_wait_for_bind(vma);
-	if (err)
-		return err;
-
-	mutex_lock(&vma->vm->mutex);
-	if (!(atomic_fetch_or(I915_VMA_GLOBAL_BIND, &vma->flags) & I915_VMA_GLOBAL_BIND)) {
-		__i915_gem_object_pin_pages(vma->obj);
-		vma->ops->bind_vma(vma->vm, NULL, vma,
-				   vma->obj->cache_level,
-				   I915_VMA_GLOBAL_BIND);
-	}
-	mutex_unlock(&vma->vm->mutex);
-
-	return 0;
+	return true;
 }
 
-static u64
-eb_relocate_entry(struct i915_execbuffer *eb,
-		  struct eb_vma *ev,
-		  const struct drm_i915_gem_relocation_entry *reloc)
+static int
+eb_relocs_check_entry(struct i915_execbuffer *eb,
+		      const struct eb_vma *ev,
+		      const struct drm_i915_gem_relocation_entry *reloc)
 {
 	struct drm_i915_private *i915 = eb->i915;
 	struct eb_vma *target;
-	int err;
 
 	/* we've already hold a reference to all valid objects */
 	target = eb_get_vma(eb, reloc->target_handle);
 	if (unlikely(!target))
 		return -ENOENT;
 
-	GEM_BUG_ON(!i915_vma_is_pinned(target->vma));
-
 	/* Validate that the target is in a valid r/w GPU domain */
 	if (unlikely(reloc->write_domain & (reloc->write_domain - 1))) {
 		drm_dbg(&i915->drm, "reloc with multiple write domains: "
-			  "target %d offset %d "
-			  "read %08x write %08x",
-			  reloc->target_handle,
-			  (int) reloc->offset,
-			  reloc->read_domains,
-			  reloc->write_domain);
+			"target %d offset %llu "
+			"read %08x write %08x",
+			reloc->target_handle,
+			reloc->offset,
+			reloc->read_domains,
+			reloc->write_domain);
 		return -EINVAL;
 	}
 	if (unlikely((reloc->write_domain | reloc->read_domains)
 		     & ~I915_GEM_GPU_DOMAINS)) {
 		drm_dbg(&i915->drm, "reloc with read/write non-GPU domains: "
-			  "target %d offset %d "
-			  "read %08x write %08x",
-			  reloc->target_handle,
-			  (int) reloc->offset,
-			  reloc->read_domains,
-			  reloc->write_domain);
+			"target %d offset %llu "
+			"read %08x write %08x",
+			reloc->target_handle,
+			reloc->offset,
+			reloc->read_domains,
+			reloc->write_domain);
 		return -EINVAL;
 	}
 
@@ -2348,130 +2097,334 @@ eb_relocate_entry(struct i915_execbuffer *eb,
 		 * batchbuffers.
 		 */
 		if (reloc->write_domain == I915_GEM_DOMAIN_INSTRUCTION &&
-		    IS_GEN(eb->i915, 6)) {
-			err = gen6_fixup_ggtt(target->vma);
-			if (err)
-				return err;
-		}
+		    IS_GEN(eb->i915, 6))
+			target->flags |= EXEC_OBJECT_NEEDS_GTT;
 	}
 
-	/*
-	 * If the relocation already has the right value in it, no
-	 * more work needs to be done.
-	 */
-	if (gen8_canonical_addr(target->vma->node.start) == reloc->presumed_offset)
-		return 0;
+	if ((int)reloc->delta < 0)
+		target->bias = max_t(u32, target->bias, -(int)reloc->delta);
 
 	/* Check that the relocation address is valid... */
 	if (unlikely(reloc->offset >
 		     ev->vma->size - (eb->reloc_cache.use_64bit_reloc ? 8 : 4))) {
 		drm_dbg(&i915->drm, "Relocation beyond object bounds: "
-			  "target %d offset %d size %d.\n",
-			  reloc->target_handle,
-			  (int)reloc->offset,
-			  (int)ev->vma->size);
+			"target %d offset %llu size %llu.\n",
+			reloc->target_handle,
+			reloc->offset,
+			ev->vma->size);
 		return -EINVAL;
 	}
 	if (unlikely(reloc->offset & 3)) {
 		drm_dbg(&i915->drm, "Relocation not 4-byte aligned: "
-			  "target %d offset %d.\n",
-			  reloc->target_handle,
-			  (int)reloc->offset);
+			"target %d offset %llu.\n",
+			reloc->target_handle,
+			reloc->offset);
 		return -EINVAL;
 	}
 
-	/*
-	 * If we write into the object, we need to force the synchronisation
-	 * barrier, either with an asynchronous clflush or if we executed the
-	 * patching using the GPU (though that should be serialised by the
-	 * timeline). To be completely sure, and since we are required to
-	 * do relocations we are already stalling, disable the user's opt
-	 * out of our synchronisation.
-	 */
-	ev->flags &= ~EXEC_OBJECT_ASYNC;
+	return 0;
+}
 
-	/* and update the user's relocation entry */
-	return relocate_entry(eb, ev->vma, reloc, target->vma);
+static struct drm_i915_gem_relocation_entry *
+eb_relocs_grow(struct i915_execbuffer *eb, unsigned long *count)
+{
+#define RELOC_SZ SZ_64K
+#define N_RELOC (RELOC_SZ / sizeof(struct drm_i915_gem_relocation_entry) - 1)
+	struct reloc_cache *c = &eb->reloc_cache;
+	struct drm_i915_gem_relocation_entry *r;
+	unsigned long remain;
+
+	GEM_BUG_ON(c->pos > N_RELOC);
+	remain = N_RELOC - c->pos;
+	if (remain == 0) {
+		struct drm_i915_gem_object *obj;
+		struct i915_vma *vma;
+		struct eb_vma *ev;
+
+		obj = i915_gem_object_create_internal(eb->i915, RELOC_SZ);
+		if (IS_ERR(obj))
+			return ERR_CAST(obj);
+
+		if (c->gen >= 6)
+			i915_gem_object_set_cache_coherency(obj,
+							    I915_CACHE_LLC);
+
+		vma = i915_vma_instance(obj, eb->context->vm, NULL);
+		if (IS_ERR(vma)) {
+			i915_gem_object_put(obj);
+			return ERR_CAST(vma);
+		}
+
+		ev = kzalloc(sizeof(*ev), GFP_KERNEL);
+		if (!ev) {
+			i915_gem_object_put(obj);
+			return ERR_PTR(-ENOMEM);
+		}
+
+		vma->private = ev;
+		ev->vma = vma;
+		ev->exec = &no_entry;
+		ev->flags = EXEC_OBJECT_SUPPORTS_48B_ADDRESS;
+		list_add_tail(&ev->bind_link, &eb->bind_list);
+		list_add(&ev->reloc_link, &eb->array->aux_list);
+
+		if (!c->head.vma) {
+			c->head.vma = vma;
+		} else {
+			struct eb_relocs_link *link;
+
+			link = (struct eb_relocs_link *)(c->map + c->pos);
+			link->vma = vma;
+		}
+
+		c->pos = 0;
+		c->map = i915_gem_object_pin_map(obj, I915_MAP_WB);
+		if (IS_ERR(c->map))
+			return ERR_CAST(c->map);
+
+		remain = N_RELOC;
+	}
+	*count = min(remain, *count);
+
+	GEM_BUG_ON(!c->map);
+	r = c->map + c->pos;
+	c->pos += *count;
+	GEM_BUG_ON(c->pos > N_RELOC);
+
+	return r;
 }
 
-static int eb_relocate_vma(struct i915_execbuffer *eb, struct eb_vma *ev)
+static int
+eb_relocs_copy_vma(struct i915_execbuffer *eb, const struct eb_vma *ev)
 {
-#define N_RELOC(x) ((x) / sizeof(struct drm_i915_gem_relocation_entry))
-	struct drm_i915_gem_relocation_entry stack[N_RELOC(512)];
 	const struct drm_i915_gem_exec_object2 *entry = ev->exec;
-	struct drm_i915_gem_relocation_entry __user *urelocs =
+	const struct drm_i915_gem_relocation_entry __user *ureloc =
 		u64_to_user_ptr(entry->relocs_ptr);
 	unsigned long remain = entry->relocation_count;
 
-	if (unlikely(remain > N_RELOC(ULONG_MAX)))
+	if (unlikely(remain > ULONG_MAX / sizeof(*ureloc)))
 		return -EINVAL;
 
-	/*
-	 * We must check that the entire relocation array is safe
-	 * to read. However, if the array is not writable the user loses
-	 * the updated relocation values.
-	 */
-	if (unlikely(!access_ok(urelocs, remain * sizeof(*urelocs))))
-		return -EFAULT;
-
 	do {
-		struct drm_i915_gem_relocation_entry *r = stack;
-		unsigned int count =
-			min_t(unsigned long, remain, ARRAY_SIZE(stack));
-		unsigned int copied;
+		struct drm_i915_gem_relocation_entry *r;
+		unsigned long count = remain;
+		int err;
 
-		/*
-		 * This is the fast path and we cannot handle a pagefault
-		 * whilst holding the struct mutex lest the user pass in the
-		 * relocations contained within a mmaped bo. For in such a case
-		 * we, the page fault handler would call i915_gem_fault() and
-		 * we would try to acquire the struct mutex again. Obviously
-		 * this is bad and so lockdep complains vehemently.
-		 */
-		copied = __copy_from_user(r, urelocs, count * sizeof(r[0]));
-		if (unlikely(copied))
+		r = eb_relocs_grow(eb, &count);
+		if (IS_ERR(r))
+			return PTR_ERR(r);
+
+		GEM_BUG_ON(!count);
+		if (unlikely(copy_from_user(r, ureloc, count * sizeof(r[0]))))
 			return -EFAULT;
 
 		remain -= count;
-		do {
-			u64 offset = eb_relocate_entry(eb, ev, r);
+		ureloc += count;
 
-			if (likely(offset == 0)) {
-			} else if ((s64)offset < 0) {
-				return (int)offset;
-			} else {
-				/*
-				 * Note that reporting an error now
-				 * leaves everything in an inconsistent
-				 * state as we have *already* changed
-				 * the relocation value inside the
-				 * object. As we have not changed the
-				 * reloc.presumed_offset or will not
-				 * change the execobject.offset, on the
-				 * call we may not rewrite the value
-				 * inside the object, leaving it
-				 * dangling and causing a GPU hang. Unless
-				 * userspace dynamically rebuilds the
-				 * relocations on each execbuf rather than
-				 * presume a static tree.
-				 *
-				 * We did previously check if the relocations
-				 * were writable (access_ok), an error now
-				 * would be a strange race with mprotect,
-				 * having already demonstrated that we
-				 * can read from this userspace address.
-				 */
-				offset = gen8_canonical_addr(offset & ~UPDATE);
-				__put_user(offset,
-					   &urelocs[r - stack].presumed_offset);
-			}
-		} while (r++, --count);
-		urelocs += ARRAY_SIZE(stack);
+		do {
+			err = eb_relocs_check_entry(eb, ev, r++);
+			if (err)
+				return err;
+		} while (--count);
 	} while (remain);
 
 	return 0;
 }
 
+static int eb_relocs_copy_user(struct i915_execbuffer *eb)
+{
+	struct eb_vma *ev;
+	int err;
+
+	/* Drop everything before we copy_from_user */
+	list_for_each_entry(ev, &eb->bind_list, bind_link)
+		eb_unreserve_vma(ev);
+
+	eb->reloc_cache.head.vma = NULL;
+	eb->reloc_cache.pos = N_RELOC;
+
+	list_for_each_entry(ev, &eb->relocs, reloc_link) {
+		err = eb_relocs_copy_vma(eb, ev);
+		if (err)
+			return err;
+	}
+
+	/* Now reacquire everything, including the extra reloc bo */
+	return eb_reserve_vm(eb);
+}
+
+static struct drm_i915_gem_relocation_entry *
+get_gpu_relocs(struct i915_execbuffer *eb,
+	       struct i915_request *rq,
+	       unsigned long *count)
+{
+	struct reloc_cache *c = &eb->reloc_cache;
+	struct drm_i915_gem_relocation_entry *r;
+	unsigned long remain;
+
+	GEM_BUG_ON(c->pos > N_RELOC);
+	remain = N_RELOC - c->pos;
+	if (remain == 0) {
+		struct eb_relocs_link link;
+		const int gen = c->gen;
+		u32 *cs;
+
+		GEM_BUG_ON(!c->head.vma);
+		GEM_BUG_ON(!c->map);
+
+		link = *(struct eb_relocs_link *)(c->map + c->pos);
+		GEM_BUG_ON(!link.vma);
+		GEM_BUG_ON(!i915_vma_is_pinned(link.vma));
+
+		cs = (u32 *)(c->map + c->pos);
+		*cs++ = MI_ARB_CHECK;
+		if (gen >= 8)
+			*cs++ = MI_BATCH_BUFFER_START_GEN8;
+		else if (gen >= 6)
+			*cs++ = MI_BATCH_BUFFER_START;
+		else
+			*cs++ = MI_BATCH_BUFFER_START | MI_BATCH_GTT;
+		*cs++ = lower_32_bits(link.vma->node.start);
+		*cs++ = upper_32_bits(link.vma->node.start);
+		i915_gem_object_flush_map(c->head.vma->obj);
+		i915_gem_object_unpin_map(c->head.vma->obj);
+
+		c->head = link;
+		c->map = NULL;
+	}
+
+	if (!c->map) {
+		struct i915_vma *vma = c->head.vma;
+		int err;
+
+		GEM_BUG_ON(!vma);
+		i915_vma_lock(vma);
+		err = i915_request_await_object(rq, vma->obj, false);
+		if (err == 0)
+			err = i915_vma_move_to_active(vma, rq, 0);
+		i915_vma_unlock(vma);
+		if (err)
+			return ERR_PTR(err);
+
+		GEM_BUG_ON(!i915_gem_object_has_pinned_pages(vma->obj));
+		c->map = page_mask_bits(vma->obj->mm.mapping);
+		c->pos = 0;
+
+		remain = N_RELOC;
+	}
+
+	*count = min(remain, *count);
+
+	GEM_BUG_ON(!c->map);
+	r = c->map + c->pos;
+	c->pos += *count;
+	GEM_BUG_ON(c->pos > N_RELOC);
+
+	return r;
+}
+
+static int eb_relocs_gpu_vma(struct i915_execbuffer *eb,
+			     struct i915_request *rq,
+			     const struct eb_vma *ev)
+{
+	const struct drm_i915_gem_exec_object2 *entry = ev->exec;
+	unsigned long remain = entry->relocation_count;
+	bool write = false;
+	int err = 0;
+
+	do {
+		struct drm_i915_gem_relocation_entry *r;
+		unsigned long count = remain;
+
+		r = get_gpu_relocs(eb, rq, &count);
+		if (IS_ERR(r))
+			return PTR_ERR(r);
+
+		GEM_BUG_ON(!count);
+		remain -= count;
+		do {
+			write |= eb_relocs_vma_entry(eb, ev, r++);
+		} while (--count);
+	} while (remain);
+
+	if (write)
+		err = reloc_move_to_gpu(rq, ev->vma);
+
+	return err;
+}
+
+static struct i915_request *reloc_gpu_alloc(struct i915_execbuffer *eb)
+{
+	struct reloc_cache *cache = &eb->reloc_cache;
+	struct i915_request *rq;
+
+	if (cache->ce == eb->context)
+		rq = __i915_request_create(cache->ce, GFP_KERNEL);
+	else
+		rq = nested_request_create(cache->ce, eb);
+	if (IS_ERR(rq))
+		return rq;
+
+	rq->cookie = lockdep_pin_lock(&i915_request_timeline(rq)->mutex);
+	return rq;
+}
+
+static int eb_relocs_gpu(struct i915_execbuffer *eb)
+{
+	struct i915_request *rq;
+	struct eb_vma *ev;
+	int err;
+
+	rq = reloc_gpu_alloc(eb);
+	if (IS_ERR(rq))
+		return PTR_ERR(rq);
+
+	rq->batch = eb->reloc_cache.head.vma;
+
+	eb->reloc_cache.map = NULL;
+	eb->reloc_cache.pos = 0;
+
+	err = 0;
+	list_for_each_entry(ev, &eb->relocs, reloc_link) {
+		err = eb_relocs_gpu_vma(eb, rq, ev);
+		if (err)
+			break;
+	}
+
+	return reloc_gpu_flush(eb, rq, err);
+}
+
+static void eb_relocs_update_vma(struct i915_execbuffer *eb, struct eb_vma *ev)
+{
+	const struct drm_i915_gem_exec_object2 *entry = ev->exec;
+	struct drm_i915_gem_relocation_entry __user *ureloc =
+		u64_to_user_ptr(entry->relocs_ptr);
+	unsigned long count = entry->relocation_count;
+
+	do {
+		u32 handle;
+
+		if (get_user(handle, &ureloc->target_handle) == 0) {
+			struct i915_vma *vma = eb_get_vma(eb, handle)->vma;
+			u64 offset = gen8_canonical_addr(vma->node.start);
+
+			if (put_user(offset, &ureloc->presumed_offset))
+				return;
+		}
+	} while (ureloc++, --count);
+}
+
+static void eb_relocs_update_user(struct i915_execbuffer *eb)
+{
+	struct eb_vma *ev;
+
+	if (!(eb->args->flags & __EXEC_HAS_RELOC))
+		return;
+
+	list_for_each_entry(ev, &eb->relocs, reloc_link)
+		eb_relocs_update_vma(eb, ev);
+}
+
 static int eb_relocate(struct i915_execbuffer *eb)
 {
 	int err;
@@ -2493,22 +2446,17 @@ static int eb_relocate(struct i915_execbuffer *eb)
 		return err;
 
 	/* The objects are in their final locations, apply the relocations. */
-	if (eb->args->flags & __EXEC_HAS_RELOC) {
-		struct eb_vma *ev;
-		int flush;
-
-		list_for_each_entry(ev, &eb->relocs, reloc_link) {
-			err = eb_relocate_vma(eb, ev);
-			if (err)
-				break;
-		}
+	if (eb->args->flags & __EXEC_HAS_RELOC && !list_empty(&eb->relocs)) {
+		err = eb_relocs_copy_user(eb);
+		if (err)
+			return err;
 
-		flush = reloc_gpu_flush(eb);
-		if (!err)
-			err = flush;
+		err = eb_relocs_gpu(eb);
+		if (err)
+			return err;
 	}
 
-	return err;
+	return 0;
 }
 
 static int eb_move_to_gpu(struct i915_execbuffer *eb)
@@ -2983,6 +2931,8 @@ static int __eb_pin_reloc_engine(struct i915_execbuffer *eb)
 		return PTR_ERR(ce);
 
 	/* Reuse eb->context->timeline with scheduler! */
+	if (engine->schedule)
+		ce->timeline = intel_timeline_get(eb->context->timeline);
 
 	i915_vm_put(ce->vm);
 	ce->vm = i915_vm_get(eb->context->vm);
@@ -3433,9 +3383,6 @@ i915_gem_do_execbuffer(struct drm_device *dev,
 	if (err)
 		goto err_vma;
 
-	/* All GPU relocation batches must be submitted prior to the user rq */
-	GEM_BUG_ON(eb.reloc_cache.rq);
-
 	/* Allocate a request for this batch buffer nice and early. */
 	eb.request = __i915_request_create(eb.context, GFP_KERNEL);
 	if (IS_ERR(eb.request)) {
@@ -3507,6 +3454,7 @@ i915_gem_do_execbuffer(struct drm_device *dev,
 err_vma:
 	if (eb.parser.shadow)
 		intel_gt_buffer_pool_put(eb.parser.shadow->vma->private);
+	eb_relocs_update_user(&eb);
 	eb_unpin_engine(&eb);
 err_context:
 	i915_gem_context_put(eb.gem_context);
diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_execbuffer.c
index 992d46db1b33..c3b94948ef9f 100644
--- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_execbuffer.c
@@ -11,7 +11,7 @@
 
 #include "mock_context.h"
 
-static u64 read_reloc(const u32 *map, int x, const u64 mask)
+static u64 read_reloc(const char *map, int x, const u64 mask)
 {
 	u64 reloc;
 
@@ -19,90 +19,111 @@ static u64 read_reloc(const u32 *map, int x, const u64 mask)
 	return reloc & mask;
 }
 
-static int __igt_gpu_reloc(struct i915_execbuffer *eb,
-			   struct drm_i915_gem_object *obj)
+static int mock_relocs_copy_user(struct i915_execbuffer *eb, struct eb_vma *ev)
 {
-	const unsigned int offsets[] = { 8, 3, 0 };
-	const u64 mask =
-		GENMASK_ULL(eb->reloc_cache.use_64bit_reloc ? 63 : 31, 0);
-	const u32 *map = page_mask_bits(obj->mm.mapping);
-	struct i915_request *rq;
-	struct i915_vma *vma;
-	int err;
-	int i;
+	const int stride = 2 * sizeof(u64);
+	struct drm_i915_gem_object *obj = ev->vma->obj;
+	void *last = NULL;
+	int n, total = 0;
+
+	eb->reloc_cache.head.vma = NULL;
+	eb->reloc_cache.pos = N_RELOC;
+
+	for (n = 0; n < obj->base.size / stride; n++) {
+		struct drm_i915_gem_relocation_entry *r;
+		unsigned long count = 1;
+
+		r = eb_relocs_grow(eb, &count);
+		if (IS_ERR(r))
+			return PTR_ERR(r);
+
+		if (!count)
+			return -EINVAL;
+
+		if (eb->reloc_cache.map != last) {
+			pr_info("%s New reloc buffer @ %d\n",
+				eb->engine->name, n);
+			last = eb->reloc_cache.map;
+			total++;
+		}
 
-	vma = i915_vma_instance(obj, eb->context->vm, NULL);
-	if (IS_ERR(vma))
-		return PTR_ERR(vma);
+		r->target_handle = 0;
+		r->offset = n * stride;
+		if (n & 1)
+			r->offset += sizeof(u32);
+		r->delta = n;
+	}
 
-	err = i915_vma_pin(vma, 0, 0, PIN_USER | PIN_HIGH);
-	if (err)
-		return err;
+	pr_info("%s: %d relocs, %d buffers\n", eb->engine->name, n, total);
 
-	/* 8-Byte aligned */
-	err = __reloc_entry_gpu(eb, vma, offsets[0] * sizeof(u32), 0);
-	if (err)
-		goto unpin_vma;
+	return n;
+}
 
-	/* !8-Byte aligned */
-	err = __reloc_entry_gpu(eb, vma, offsets[1] * sizeof(u32), 1);
+static int check_relocs(struct i915_execbuffer *eb, struct eb_vma *ev)
+{
+	const u64 mask =
+		GENMASK_ULL(eb->reloc_cache.use_64bit_reloc ? 63 : 31, 0);
+	const int stride = 2 * sizeof(u64);
+	struct drm_i915_gem_object *obj = ev->vma->obj;
+	const void *map = __i915_gem_object_mapping(obj);
+	int n, err = 0;
+
+	for (n = 0; n < obj->base.size / stride; n++) {
+		unsigned int offset;
+		u64 address, reloc;
+
+		address = gen8_canonical_addr(ev->vma->node.start + n);
+		address &= mask;
+
+		offset = n * stride;
+		if (n & 1)
+			offset += sizeof(u32);
+
+		reloc = read_reloc(map, offset, mask);
+		if (reloc != address) {
+			pr_err("%s[%d]: map[%x] %llx != %llx\n",
+			       eb->engine->name, n, offset, reloc, address);
+			err = -EINVAL;
+		}
+	}
 	if (err)
-		goto unpin_vma;
+		igt_hexdump(map, obj->base.size);
+
+	return err;
+}
 
-	/* Skip to the end of the cmd page */
-	i = PAGE_SIZE / sizeof(u32) - RELOC_TAIL - 1;
-	i -= eb->reloc_cache.rq_size;
-	memset32(eb->reloc_cache.rq_cmd + eb->reloc_cache.rq_size,
-		 MI_NOOP, i);
-	eb->reloc_cache.rq_size += i;
+static int __igt_gpu_reloc(struct i915_execbuffer *eb, struct eb_vma *ev)
+{
+	int err;
+
+	err = mock_relocs_copy_user(eb, ev);
+	if (err < 0)
+		return err;
+	ev->exec->relocation_count = err;
 
-	/* Force batch chaining */
-	err = __reloc_entry_gpu(eb, vma, offsets[2] * sizeof(u32), 2);
+	err = eb_reserve_vm(eb);
 	if (err)
-		goto unpin_vma;
+		return err;
 
-	GEM_BUG_ON(!eb->reloc_cache.rq);
-	rq = i915_request_get(eb->reloc_cache.rq);
-	err = reloc_gpu_flush(eb);
+	err = eb_relocs_gpu(eb);
 	if (err)
-		goto put_rq;
-	GEM_BUG_ON(eb->reloc_cache.rq);
+		return err;
 
-	err = i915_gem_object_wait(obj, I915_WAIT_INTERRUPTIBLE, HZ / 2);
-	if (err) {
+	if (i915_gem_object_wait(ev->vma->obj,
+				 I915_WAIT_INTERRUPTIBLE, HZ / 2)) {
 		intel_gt_set_wedged(eb->engine->gt);
-		goto put_rq;
+		return -EIO;
 	}
 
-	if (!i915_request_completed(rq)) {
-		pr_err("%s: did not wait for relocations!\n", eb->engine->name);
-		err = -EINVAL;
-		goto put_rq;
-	}
-
-	for (i = 0; i < ARRAY_SIZE(offsets); i++) {
-		u64 reloc = read_reloc(map, offsets[i], mask);
-
-		if (reloc != i) {
-			pr_err("%s[%d]: map[%d] %llx != %x\n",
-			       eb->engine->name, i, offsets[i], reloc, i);
-			err = -EINVAL;
-		}
-	}
-	if (err)
-		igt_hexdump(map, 4096);
-
-put_rq:
-	i915_request_put(rq);
-unpin_vma:
-	i915_vma_unpin(vma);
-	return err;
+	return check_relocs(eb, ev);
 }
 
 static int igt_gpu_reloc(void *arg)
 {
 	struct i915_execbuffer eb;
 	struct drm_i915_gem_object *scratch;
+	struct drm_i915_gem_exec_object2 exec;
+	struct eb_vma ev = { .exec = &exec };
 	struct file *file;
 	int err = 0;
 	u32 *map;
@@ -112,11 +133,13 @@ static int igt_gpu_reloc(void *arg)
 		return PTR_ERR(file);
 
 	eb.i915 = arg;
+	INIT_LIST_HEAD(&eb.relocs);
+
 	eb.gem_context = live_context(arg, file);
 	if (IS_ERR(eb.gem_context))
 		goto err_file;
 
-	scratch = i915_gem_object_create_internal(eb.i915, 4096);
+	scratch = i915_gem_object_create_internal(eb.i915, SZ_32K);
 	if (IS_ERR(scratch))
 		goto err_file;
 
@@ -126,33 +149,65 @@ static int igt_gpu_reloc(void *arg)
 		goto err_scratch;
 	}
 
+	eb.lut_size = -1;
+	eb.vma = &ev;
+	list_add(&ev.reloc_link, &eb.relocs);
+	GEM_BUG_ON(eb_get_vma(&eb, 0) != &ev);
+
 	for_each_uabi_engine(eb.engine, eb.i915) {
+		INIT_LIST_HEAD(&eb.bind_list);
 		reloc_cache_init(&eb.reloc_cache, eb.i915);
-		memset(map, POISON_INUSE, 4096);
+		memset(map, POISON_INUSE, scratch->base.size);
+		wmb();
 
 		intel_engine_pm_get(eb.engine);
+
+		eb.array = eb_vma_array_create(1);
+		if (!eb.array) {
+			err = -ENOMEM;
+			goto err_pm;
+		}
+
 		eb.context = intel_context_create(eb.engine);
 		if (IS_ERR(eb.context)) {
 			err = PTR_ERR(eb.context);
-			goto err_pm;
+			goto err_array;
 		}
 
 		err = intel_context_pin(eb.context);
 		if (err)
 			goto err_put;
 
+		ev.vma = i915_vma_instance(scratch, eb.context->vm, NULL);
+		if (IS_ERR(ev.vma)) {
+			err = PTR_ERR(ev.vma);
+			goto err_unpin;
+		}
+
+		err = i915_vma_pin(ev.vma, 0, 0, PIN_USER | PIN_HIGH);
+		if (err)
+			goto err_unpin;
+
 		mutex_lock(&eb.context->timeline->mutex);
 		intel_context_enter(eb.context);
-		eb.reloc_cache.ce = eb.context;
 
-		err = __igt_gpu_reloc(&eb, scratch);
+		err = __eb_pin_reloc_engine(&eb);
+		if (err)
+			goto err_exit;
 
+		err = __igt_gpu_reloc(&eb, &ev);
+
+		__eb_unpin_reloc_engine(&eb);
+err_exit:
 		intel_context_exit(eb.context);
 		mutex_unlock(&eb.context->timeline->mutex);
-
+		i915_vma_unpin(ev.vma);
+err_unpin:
 		intel_context_unpin(eb.context);
 err_put:
 		intel_context_put(eb.context);
+err_array:
+		eb_vma_array_put(eb.array);
 err_pm:
 		intel_engine_pm_put(eb.engine);
 		if (err)
-- 
2.20.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [Intel-gfx] [PATCH 17/20] drm/i915: Add an implementation for i915_gem_ww_ctx locking, v2.
  2020-07-06  6:19 [Intel-gfx] s/obj->mm.lock// Chris Wilson
                   ` (15 preceding siblings ...)
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 16/20] drm/i915/gem: Reintroduce multiple passes for reloc processing Chris Wilson
@ 2020-07-06  6:19 ` Chris Wilson
  2020-07-06 17:21   ` kernel test robot
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 18/20] drm/i915/gem: Pull execbuf dma resv under a single critical section Chris Wilson
                   ` (7 subsequent siblings)
  24 siblings, 1 reply; 65+ messages in thread
From: Chris Wilson @ 2020-07-06  6:19 UTC (permalink / raw)
  To: intel-gfx

From: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>

i915_gem_ww_ctx is used to lock all gem bo's for pinning and memory
eviction. We don't use it yet, but lets start adding the definition
first.

To use it, we have to pass a non-NULL ww to gem_object_lock, and don't
unlock directly. It is done in i915_gem_ww_ctx_fini.

Changes since v1:
- Change ww_ctx and obj order in locking functions (Jonas Lahtinen)

Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
---
 drivers/gpu/drm/i915/Makefile                 |   4 +
 drivers/gpu/drm/i915/i915_globals.c           |   1 +
 drivers/gpu/drm/i915/i915_globals.h           |   1 +
 drivers/gpu/drm/i915/mm/i915_acquire_ctx.c    | 139 ++++++++++
 drivers/gpu/drm/i915/mm/i915_acquire_ctx.h    |  34 +++
 drivers/gpu/drm/i915/mm/st_acquire_ctx.c      | 242 ++++++++++++++++++
 .../drm/i915/selftests/i915_mock_selftests.h  |   1 +
 7 files changed, 422 insertions(+)
 create mode 100644 drivers/gpu/drm/i915/mm/i915_acquire_ctx.c
 create mode 100644 drivers/gpu/drm/i915/mm/i915_acquire_ctx.h
 create mode 100644 drivers/gpu/drm/i915/mm/st_acquire_ctx.c

diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
index 41a27fd5dbc7..33c85b4ff3ed 100644
--- a/drivers/gpu/drm/i915/Makefile
+++ b/drivers/gpu/drm/i915/Makefile
@@ -124,6 +124,10 @@ gt-y += \
 	gt/gen9_renderstate.o
 i915-y += $(gt-y)
 
+# Memory + DMA management
+i915-y += \
+	mm/i915_acquire_ctx.o
+
 # GEM (Graphics Execution Management) code
 gem-y += \
 	gem/i915_gem_busy.o \
diff --git a/drivers/gpu/drm/i915/i915_globals.c b/drivers/gpu/drm/i915/i915_globals.c
index 3aa213684293..51ec42a14694 100644
--- a/drivers/gpu/drm/i915/i915_globals.c
+++ b/drivers/gpu/drm/i915/i915_globals.c
@@ -87,6 +87,7 @@ static void __i915_globals_cleanup(void)
 
 static __initconst int (* const initfn[])(void) = {
 	i915_global_active_init,
+	i915_global_acquire_init,
 	i915_global_buddy_init,
 	i915_global_context_init,
 	i915_global_gem_context_init,
diff --git a/drivers/gpu/drm/i915/i915_globals.h b/drivers/gpu/drm/i915/i915_globals.h
index b2f5cd9b9b1a..11227abf2769 100644
--- a/drivers/gpu/drm/i915/i915_globals.h
+++ b/drivers/gpu/drm/i915/i915_globals.h
@@ -27,6 +27,7 @@ void i915_globals_exit(void);
 
 /* constructors */
 int i915_global_active_init(void);
+int i915_global_acquire_init(void);
 int i915_global_buddy_init(void);
 int i915_global_context_init(void);
 int i915_global_gem_context_init(void);
diff --git a/drivers/gpu/drm/i915/mm/i915_acquire_ctx.c b/drivers/gpu/drm/i915/mm/i915_acquire_ctx.c
new file mode 100644
index 000000000000..d1c3b958c15d
--- /dev/null
+++ b/drivers/gpu/drm/i915/mm/i915_acquire_ctx.c
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2020 Intel Corporation
+ */
+
+#include <linux/dma-resv.h>
+
+#include "i915_globals.h"
+#include "gem/i915_gem_object.h"
+
+#include "i915_acquire_ctx.h"
+
+static struct i915_global_acquire {
+	struct i915_global base;
+	struct kmem_cache *slab_acquires;
+} global;
+
+struct i915_acquire {
+	struct drm_i915_gem_object *obj;
+	struct i915_acquire *next;
+};
+
+static struct i915_acquire *i915_acquire_alloc(void)
+{
+	return kmem_cache_alloc(global.slab_acquires, GFP_KERNEL);
+}
+
+static void i915_acquire_free(struct i915_acquire *lnk)
+{
+	kmem_cache_free(global.slab_acquires, lnk);
+}
+
+void i915_acquire_ctx_init(struct i915_acquire_ctx *ctx)
+{
+	ww_acquire_init(&ctx->ctx, &reservation_ww_class);
+	ctx->locked = NULL;
+}
+
+int i915_acquire_ctx_lock(struct i915_acquire_ctx *ctx,
+			  struct drm_i915_gem_object *obj)
+{
+	struct i915_acquire *lock, *lnk;
+	int err;
+
+	lock = i915_acquire_alloc();
+	if (!lock)
+		return -ENOMEM;
+
+	lock->obj = i915_gem_object_get(obj);
+	lock->next = NULL;
+
+	while ((lnk = lock)) {
+		obj = lnk->obj;
+		lock = lnk->next;
+
+		err = dma_resv_lock_interruptible(obj->base.resv, &ctx->ctx);
+		if (err == -EDEADLK) {
+			struct i915_acquire *old;
+
+			while ((old = ctx->locked)) {
+				i915_gem_object_unlock(old->obj);
+				ctx->locked = old->next;
+				old->next = lock;
+				lock = old;
+			}
+
+			err = dma_resv_lock_slow_interruptible(obj->base.resv,
+							       &ctx->ctx);
+		}
+		if (!err) {
+			lnk->next = ctx->locked;
+			ctx->locked = lnk;
+		} else {
+			i915_gem_object_put(obj);
+			i915_acquire_free(lnk);
+		}
+		if (err == -EALREADY)
+			err = 0;
+		if (err)
+			break;
+	}
+
+	while ((lnk = lock)) {
+		lock = lnk->next;
+		i915_gem_object_put(lnk->obj);
+		i915_acquire_free(lnk);
+	}
+
+	return err;
+}
+
+int i915_acquire_mm(struct i915_acquire_ctx *acquire)
+{
+	return 0;
+}
+
+void i915_acquire_ctx_fini(struct i915_acquire_ctx *ctx)
+{
+	struct i915_acquire *lnk;
+
+	while ((lnk = ctx->locked)) {
+		i915_gem_object_unlock(lnk->obj);
+		i915_gem_object_put(lnk->obj);
+
+		ctx->locked = lnk->next;
+		i915_acquire_free(lnk);
+	}
+
+	ww_acquire_fini(&ctx->ctx);
+}
+
+#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
+#include "st_acquire_ctx.c"
+#endif
+
+static void i915_global_acquire_shrink(void)
+{
+	kmem_cache_shrink(global.slab_acquires);
+}
+
+static void i915_global_acquire_exit(void)
+{
+	kmem_cache_destroy(global.slab_acquires);
+}
+
+static struct i915_global_acquire global = { {
+	.shrink = i915_global_acquire_shrink,
+	.exit = i915_global_acquire_exit,
+} };
+
+int __init i915_global_acquire_init(void)
+{
+	global.slab_acquires = KMEM_CACHE(i915_acquire, 0);
+	if (!global.slab_acquires)
+		return -ENOMEM;
+
+	i915_global_register(&global.base);
+	return 0;
+}
diff --git a/drivers/gpu/drm/i915/mm/i915_acquire_ctx.h b/drivers/gpu/drm/i915/mm/i915_acquire_ctx.h
new file mode 100644
index 000000000000..bea00e3d6a36
--- /dev/null
+++ b/drivers/gpu/drm/i915/mm/i915_acquire_ctx.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2020 Intel Corporation
+ */
+
+#ifndef __I915_ACQIURE_CTX_H__
+#define __I915_ACQUIRE_CTX_H__
+
+#include <linux/list.h>
+#include <linux/ww_mutex.h>
+
+struct drm_i915_gem_object;
+struct i915_acquire;
+
+struct i915_acquire_ctx {
+	struct ww_acquire_ctx ctx;
+	struct i915_acquire *locked;
+};
+
+void i915_acquire_ctx_init(struct i915_acquire_ctx *acquire);
+
+static inline void i915_acquire_ctx_done(struct i915_acquire_ctx *acquire)
+{
+	ww_acquire_done(&acquire->ctx);
+}
+
+void i915_acquire_ctx_fini(struct i915_acquire_ctx *acquire);
+
+int __must_check i915_acquire_ctx_lock(struct i915_acquire_ctx *acquire,
+				       struct drm_i915_gem_object *obj);
+
+int i915_acquire_mm(struct i915_acquire_ctx *acquire);
+
+#endif /* __I915_ACQUIRE_CTX_H__ */
diff --git a/drivers/gpu/drm/i915/mm/st_acquire_ctx.c b/drivers/gpu/drm/i915/mm/st_acquire_ctx.c
new file mode 100644
index 000000000000..6e94bdbb3265
--- /dev/null
+++ b/drivers/gpu/drm/i915/mm/st_acquire_ctx.c
@@ -0,0 +1,242 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2020 Intel Corporation
+ */
+
+#include "i915_drv.h"
+#include "i915_selftest.h"
+
+#include "selftests/i915_random.h"
+#include "selftests/mock_gem_device.h"
+
+static int checked_acquire_lock(struct i915_acquire_ctx *acquire,
+				struct drm_i915_gem_object *obj,
+				const char *name)
+{
+	int err;
+
+	err = i915_acquire_ctx_lock(acquire, obj);
+	if (err) {
+		pr_err("i915_acquire_lock(%s) failed, err:%d\n", name, err);
+		return err;
+	}
+
+	if (!mutex_is_locked(&obj->base.resv->lock.base)) {
+		pr_err("Failed to lock %s!\n", name);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int igt_acquire_lock(void *arg)
+{
+	struct drm_i915_private *i915 = arg;
+	struct drm_i915_gem_object *a, *b;
+	struct i915_acquire_ctx acquire;
+	int err;
+
+	a = i915_gem_object_create_internal(i915, PAGE_SIZE);
+	if (IS_ERR(a))
+		return PTR_ERR(a);
+
+	b = i915_gem_object_create_internal(i915, PAGE_SIZE);
+	if (IS_ERR(b)) {
+		err = PTR_ERR(b);
+		goto out_a;
+	}
+
+	i915_acquire_ctx_init(&acquire);
+
+	err = checked_acquire_lock(&acquire, a, "A");
+	if (err)
+		goto out_fini;
+
+	err = checked_acquire_lock(&acquire, b, "B");
+	if (err)
+		goto out_fini;
+
+	/* Again for EALREADY */
+
+	err = checked_acquire_lock(&acquire, a, "A");
+	if (err)
+		goto out_fini;
+
+	err = checked_acquire_lock(&acquire, b, "B");
+	if (err)
+		goto out_fini;
+
+	i915_acquire_ctx_done(&acquire);
+
+	if (!mutex_is_locked(&a->base.resv->lock.base)) {
+		pr_err("Failed to lock A, after i915_acquire_done\n");
+		err = -EINVAL;
+	}
+	if (!mutex_is_locked(&b->base.resv->lock.base)) {
+		pr_err("Failed to lock B, after i915_acquire_done\n");
+		err = -EINVAL;
+	}
+
+out_fini:
+	i915_acquire_ctx_fini(&acquire);
+
+	if (mutex_is_locked(&a->base.resv->lock.base)) {
+		pr_err("A is still locked!\n");
+		err = -EINVAL;
+	}
+	if (mutex_is_locked(&b->base.resv->lock.base)) {
+		pr_err("B is still locked!\n");
+		err = -EINVAL;
+	}
+
+	i915_gem_object_put(b);
+out_a:
+	i915_gem_object_put(a);
+	return err;
+}
+
+struct deadlock {
+	struct drm_i915_gem_object *obj[64];
+};
+
+static int __igt_acquire_deadlock(void *arg)
+{
+	struct deadlock *dl = arg;
+	const unsigned int total = ARRAY_SIZE(dl->obj);
+	I915_RND_STATE(prng);
+	unsigned int *order;
+	int n, count, err = 0;
+
+	order = i915_random_order(total, &prng);
+	if (!order)
+		return -ENOMEM;
+
+	while (!kthread_should_stop()) {
+		struct i915_acquire_ctx acquire;
+
+		i915_random_reorder(order, total, &prng);
+		count = i915_prandom_u32_max_state(total, &prng);
+
+		i915_acquire_ctx_init(&acquire);
+
+		for (n = 0; n < count; n++) {
+			struct drm_i915_gem_object *obj = dl->obj[order[n]];
+
+			err = checked_acquire_lock(&acquire, obj, "dl");
+			if (err) {
+				i915_acquire_ctx_fini(&acquire);
+				goto out;
+			}
+		}
+
+		i915_acquire_ctx_done(&acquire);
+
+#if IS_ENABLED(CONFIG_LOCKDEP)
+		for (n = 0; n < count; n++) {
+			struct drm_i915_gem_object *obj = dl->obj[order[n]];
+
+			if (!lockdep_is_held(&obj->base.resv->lock.base)) {
+				pr_err("lock not taken!\n");
+				i915_acquire_ctx_fini(&acquire);
+				err = -EINVAL;
+				goto out;
+			}
+		}
+#endif
+
+		i915_acquire_ctx_fini(&acquire);
+
+#if IS_ENABLED(CONFIG_LOCKDEP)
+		for (n = 0; n < count; n++) {
+			struct drm_i915_gem_object *obj = dl->obj[order[n]];
+
+			if (lockdep_is_held(&obj->base.resv->lock.base)) {
+				pr_err("lock still held after fini!\n");
+				err = -EINVAL;
+				goto out;
+			}
+		}
+#endif
+	}
+
+out:
+	kfree(order);
+	return err;
+}
+
+static int igt_acquire_deadlock(void *arg)
+{
+	unsigned int ncpus = num_online_cpus();
+	struct drm_i915_private *i915 = arg;
+	struct task_struct **threads;
+	struct deadlock dl;
+	int ret = 0, n;
+
+	threads = kcalloc(ncpus, sizeof(*threads), GFP_KERNEL);
+	if (!threads)
+		return -ENOMEM;
+
+	for (n = 0; n < ARRAY_SIZE(dl.obj); n += 2) {
+		dl.obj[n] = i915_gem_object_create_internal(i915, PAGE_SIZE);
+		if (IS_ERR(dl.obj[n])) {
+			ret = PTR_ERR(dl.obj[n]);
+			goto out_obj;
+		}
+
+		/* Repeat the objects for -EALREADY */
+		dl.obj[n + 1] = i915_gem_object_get(dl.obj[n]);
+	}
+
+	for (n = 0; n < ncpus; n++) {
+		threads[n] = kthread_run(__igt_acquire_deadlock,
+					 &dl, "igt/%d", n);
+		if (IS_ERR(threads[n])) {
+			ret = PTR_ERR(threads[n]);
+			ncpus = n;
+			break;
+		}
+
+		get_task_struct(threads[n]);
+	}
+
+	yield(); /* start all threads before we begin */
+	msleep(jiffies_to_msecs(i915_selftest.timeout_jiffies));
+
+	for (n = 0; n < ncpus; n++) {
+		int err;
+
+		err = kthread_stop(threads[n]);
+		if (err < 0 && !ret)
+			ret = err;
+
+		put_task_struct(threads[n]);
+	}
+
+out_obj:
+	for (n = 0; n < ARRAY_SIZE(dl.obj); n++) {
+		if (IS_ERR(dl.obj[n]))
+			break;
+		i915_gem_object_put(dl.obj[n]);
+	}
+	kfree(threads);
+	return ret;
+}
+
+int i915_acquire_mock_selftests(void)
+{
+	static const struct i915_subtest tests[] = {
+		SUBTEST(igt_acquire_lock),
+		SUBTEST(igt_acquire_deadlock),
+	};
+	struct drm_i915_private *i915;
+	int err = 0;
+
+	i915 = mock_gem_device();
+	if (!i915)
+		return -ENOMEM;
+
+	err = i915_subtests(tests, i915);
+	drm_dev_put(&i915->drm);
+
+	return err;
+}
diff --git a/drivers/gpu/drm/i915/selftests/i915_mock_selftests.h b/drivers/gpu/drm/i915/selftests/i915_mock_selftests.h
index 3db34d3eea58..cb6f94633356 100644
--- a/drivers/gpu/drm/i915/selftests/i915_mock_selftests.h
+++ b/drivers/gpu/drm/i915/selftests/i915_mock_selftests.h
@@ -26,6 +26,7 @@ selftest(engine, intel_engine_cs_mock_selftests)
 selftest(timelines, intel_timeline_mock_selftests)
 selftest(requests, i915_request_mock_selftests)
 selftest(objects, i915_gem_object_mock_selftests)
+selftest(acquire, i915_acquire_mock_selftests)
 selftest(phys, i915_gem_phys_mock_selftests)
 selftest(dmabuf, i915_gem_dmabuf_mock_selftests)
 selftest(vma, i915_vma_mock_selftests)
-- 
2.20.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [Intel-gfx] [PATCH 18/20] drm/i915/gem: Pull execbuf dma resv under a single critical section
  2020-07-06  6:19 [Intel-gfx] s/obj->mm.lock// Chris Wilson
                   ` (16 preceding siblings ...)
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 17/20] drm/i915: Add an implementation for i915_gem_ww_ctx locking, v2 Chris Wilson
@ 2020-07-06  6:19 ` Chris Wilson
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 19/20] drm/i915/gem: Replace i915_gem_object.mm.mutex with reservation_ww_class Chris Wilson
                   ` (6 subsequent siblings)
  24 siblings, 0 replies; 65+ messages in thread
From: Chris Wilson @ 2020-07-06  6:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: Chris Wilson

Acquire all the objects and their backing storage, and page directories,
as used by execbuf under a single common ww_mutex. Albeit we have to
restart the critical section a few times in order to handle various
restrictions (such as avoiding copy_(from|to)_user and mmap_sem).

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 .../gpu/drm/i915/gem/i915_gem_execbuffer.c    | 157 ++++++++----------
 .../i915/gem/selftests/i915_gem_execbuffer.c  |   8 +-
 2 files changed, 78 insertions(+), 87 deletions(-)

diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
index fbf5c5cd51ca..bcd100f8a6c7 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
@@ -20,6 +20,7 @@
 #include "gt/intel_gt_pm.h"
 #include "gt/intel_gt_requests.h"
 #include "gt/intel_ring.h"
+#include "mm/i915_acquire_ctx.h"
 
 #include "i915_drv.h"
 #include "i915_gem_clflush.h"
@@ -244,6 +245,8 @@ struct i915_execbuffer {
 	struct intel_context *context; /* logical state for the request */
 	struct i915_gem_context *gem_context; /** caller's context */
 
+	struct i915_acquire_ctx acquire; /** lock for _all_ DMA reservations */
+
 	struct i915_request *request; /** our request to build */
 	struct eb_vma *batch; /** identity of the batch obj/vma */
 
@@ -386,42 +389,6 @@ static void eb_vma_array_put(struct eb_vma_array *arr)
 	kref_put(&arr->kref, eb_vma_array_destroy);
 }
 
-static int
-eb_lock_vma(struct i915_execbuffer *eb, struct ww_acquire_ctx *acquire)
-{
-	struct eb_vma *ev;
-	int err = 0;
-
-	list_for_each_entry(ev, &eb->submit_list, submit_link) {
-		struct i915_vma *vma = ev->vma;
-
-		err = ww_mutex_lock_interruptible(&vma->resv->lock, acquire);
-		if (err == -EDEADLK) {
-			struct eb_vma *unlock = ev, *en;
-
-			list_for_each_entry_safe_continue_reverse(unlock, en,
-								  &eb->submit_list,
-								  submit_link) {
-				ww_mutex_unlock(&unlock->vma->resv->lock);
-				list_move_tail(&unlock->submit_link, &eb->submit_list);
-			}
-
-			GEM_BUG_ON(!list_is_first(&ev->submit_link, &eb->submit_list));
-			err = ww_mutex_lock_slow_interruptible(&vma->resv->lock,
-							       acquire);
-		}
-		if (err) {
-			list_for_each_entry_continue_reverse(ev,
-							     &eb->submit_list,
-							     submit_link)
-				ww_mutex_unlock(&ev->vma->resv->lock);
-			break;
-		}
-	}
-
-	return err;
-}
-
 static int eb_create(struct i915_execbuffer *eb)
 {
 	/* Allocate an extra slot for use by the sentinel */
@@ -665,6 +632,25 @@ eb_add_vma(struct i915_execbuffer *eb,
 	}
 }
 
+static int eb_lock_mm(struct i915_execbuffer *eb)
+{
+	struct eb_vma *ev;
+	int err;
+
+	list_for_each_entry(ev, &eb->bind_list, bind_link) {
+		err = i915_acquire_ctx_lock(&eb->acquire, ev->vma->obj);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+static int eb_acquire_mm(struct i915_execbuffer *eb)
+{
+	return i915_acquire_mm(&eb->acquire);
+}
+
 struct eb_vm_work {
 	struct dma_fence_work base;
 	struct eb_vma_array *array;
@@ -1390,7 +1376,15 @@ static int eb_reserve_vm(struct i915_execbuffer *eb)
 	unsigned long count;
 	struct eb_vma *ev;
 	unsigned int pass;
-	int err = 0;
+	int err;
+
+	err = eb_lock_mm(eb);
+	if (err)
+		return err;
+
+	err = eb_acquire_mm(eb);
+	if (err)
+		return err;
 
 	count = 0;
 	INIT_LIST_HEAD(&unbound);
@@ -1416,10 +1410,19 @@ static int eb_reserve_vm(struct i915_execbuffer *eb)
 	if (count == 0)
 		return 0;
 
+	/* We need to reserve page directories, release all, start over */
+	i915_acquire_ctx_fini(&eb->acquire);
+
 	pass = 0;
 	do {
 		struct eb_vm_work *work;
 
+		i915_acquire_ctx_init(&eb->acquire);
+
+		err = eb_lock_mm(eb);
+		if (err)
+			return err;
+
 		work = eb_vm_work(eb, count);
 		if (!work)
 			return -ENOMEM;
@@ -1437,6 +1440,10 @@ static int eb_reserve_vm(struct i915_execbuffer *eb)
 			}
 		}
 
+		err = eb_acquire_mm(eb);
+		if (err)
+			return eb_vm_work_cancel(work, err);
+
 		err = i915_vm_pin_pt_stash(work->vm, &work->stash);
 		if (err)
 			return eb_vm_work_cancel(work, err);
@@ -1527,6 +1534,8 @@ static int eb_reserve_vm(struct i915_execbuffer *eb)
 		if (signal_pending(current))
 			return -EINTR;
 
+		i915_acquire_ctx_fini(&eb->acquire);
+
 		/* Now safe to wait with no reservations held */
 
 		if (err == -EAGAIN) {
@@ -1535,8 +1544,10 @@ static int eb_reserve_vm(struct i915_execbuffer *eb)
 		}
 
 		err = wait_for_unbinds(eb, &unbound, pass++);
-		if (err)
+		if (err) {
+			i915_acquire_ctx_init(&eb->acquire);
 			return err;
+		}
 	} while (1);
 }
 
@@ -1951,8 +1962,6 @@ static int reloc_move_to_gpu(struct i915_request *rq, struct i915_vma *vma)
 	struct drm_i915_gem_object *obj = vma->obj;
 	int err;
 
-	i915_vma_lock(vma);
-
 	if (obj->cache_dirty & ~obj->cache_coherent)
 		i915_gem_clflush_object(obj, 0);
 	obj->write_domain = 0;
@@ -1961,8 +1970,6 @@ static int reloc_move_to_gpu(struct i915_request *rq, struct i915_vma *vma)
 	if (err == 0)
 		err = i915_vma_move_to_active(vma, rq, EXEC_OBJECT_WRITE);
 
-	i915_vma_unlock(vma);
-
 	return err;
 }
 
@@ -2234,11 +2241,12 @@ eb_relocs_copy_vma(struct i915_execbuffer *eb, const struct eb_vma *ev)
 static int eb_relocs_copy_user(struct i915_execbuffer *eb)
 {
 	struct eb_vma *ev;
-	int err;
+	int err = 0;
 
 	/* Drop everything before we copy_from_user */
 	list_for_each_entry(ev, &eb->bind_list, bind_link)
 		eb_unreserve_vma(ev);
+	i915_acquire_ctx_fini(&eb->acquire);
 
 	eb->reloc_cache.head.vma = NULL;
 	eb->reloc_cache.pos = N_RELOC;
@@ -2246,9 +2254,13 @@ static int eb_relocs_copy_user(struct i915_execbuffer *eb)
 	list_for_each_entry(ev, &eb->relocs, reloc_link) {
 		err = eb_relocs_copy_vma(eb, ev);
 		if (err)
-			return err;
+			break;
 	}
 
+	i915_acquire_ctx_init(&eb->acquire);
+	if (err)
+		return err;
+
 	/* Now reacquire everything, including the extra reloc bo */
 	return eb_reserve_vm(eb);
 }
@@ -2298,11 +2310,9 @@ get_gpu_relocs(struct i915_execbuffer *eb,
 		int err;
 
 		GEM_BUG_ON(!vma);
-		i915_vma_lock(vma);
 		err = i915_request_await_object(rq, vma->obj, false);
 		if (err == 0)
 			err = i915_vma_move_to_active(vma, rq, 0);
-		i915_vma_unlock(vma);
 		if (err)
 			return ERR_PTR(err);
 
@@ -2461,17 +2471,8 @@ static int eb_relocate(struct i915_execbuffer *eb)
 
 static int eb_move_to_gpu(struct i915_execbuffer *eb)
 {
-	struct ww_acquire_ctx acquire;
 	struct eb_vma *ev;
-	int err = 0;
-
-	ww_acquire_init(&acquire, &reservation_ww_class);
-
-	err = eb_lock_vma(eb, &acquire);
-	if (err)
-		goto err_fini;
-
-	ww_acquire_done(&acquire);
+	int err;
 
 	list_for_each_entry(ev, &eb->submit_list, submit_link) {
 		struct i915_vma *vma = ev->vma;
@@ -2510,27 +2511,22 @@ static int eb_move_to_gpu(struct i915_execbuffer *eb)
 				flags &= ~EXEC_OBJECT_ASYNC;
 		}
 
-		if (err == 0 && !(flags & EXEC_OBJECT_ASYNC)) {
+		if (!(flags & EXEC_OBJECT_ASYNC)) {
 			err = i915_request_await_object
 				(eb->request, obj, flags & EXEC_OBJECT_WRITE);
+			if (unlikely(err))
+				goto err_skip;
 		}
 
-		if (err == 0)
-			err = i915_vma_move_to_active(vma, eb->request, flags);
-
-		i915_vma_unlock(vma);
+		err = i915_vma_move_to_active(vma, eb->request, flags);
+		if (unlikely(err))
+			goto err_skip;
 	}
-	ww_acquire_fini(&acquire);
-
-	if (unlikely(err))
-		goto err_skip;
 
 	/* Unconditionally flush any chipset caches (for streaming writes). */
 	intel_gt_chipset_flush(eb->engine->gt);
 	return 0;
 
-err_fini:
-	ww_acquire_fini(&acquire);
 err_skip:
 	i915_request_set_error_once(eb->request, err);
 	return err;
@@ -2704,39 +2700,27 @@ static int eb_parse_pipeline(struct i915_execbuffer *eb,
 	/* Mark active refs early for this worker, in case we get interrupted */
 	err = parser_mark_active(pw, eb->context->timeline);
 	if (err)
-		goto err_commit;
-
-	err = dma_resv_lock_interruptible(pw->batch->resv, NULL);
-	if (err)
-		goto err_commit;
+		goto out;
 
 	err = dma_resv_reserve_shared(pw->batch->resv, 1);
 	if (err)
-		goto err_commit_unlock;
+		goto out;
 
 	/* Wait for all writes (and relocs) into the batch to complete */
 	err = i915_sw_fence_await_reservation(&pw->base.chain,
 					      pw->batch->resv, NULL, false,
 					      0, I915_FENCE_GFP);
 	if (err < 0)
-		goto err_commit_unlock;
+		goto out;
 
 	/* Keep the batch alive and unwritten as we parse */
 	dma_resv_add_shared_fence(pw->batch->resv, &pw->base.dma);
 
-	dma_resv_unlock(pw->batch->resv);
-
 	/* Force execution to wait for completion of the parser */
-	dma_resv_lock(shadow->resv, NULL);
 	dma_resv_add_excl_fence(shadow->resv, &pw->base.dma);
-	dma_resv_unlock(shadow->resv);
-
-	dma_fence_work_commit_imm(&pw->base);
-	return 0;
 
-err_commit_unlock:
-	dma_resv_unlock(pw->batch->resv);
-err_commit:
+	err = 0;
+out:
 	i915_sw_fence_set_error_once(&pw->base.chain, err);
 	dma_fence_work_commit_imm(&pw->base);
 	return err;
@@ -3366,6 +3350,8 @@ i915_gem_do_execbuffer(struct drm_device *dev,
 		goto err_context;
 	lockdep_assert_held(&eb.context->timeline->mutex);
 
+	i915_acquire_ctx_init(&eb.acquire);
+
 	err = eb_relocate(&eb);
 	if (err) {
 		/*
@@ -3379,6 +3365,8 @@ i915_gem_do_execbuffer(struct drm_device *dev,
 		goto err_vma;
 	}
 
+	i915_acquire_ctx_done(&eb.acquire);
+
 	err = eb_parse(&eb);
 	if (err)
 		goto err_vma;
@@ -3454,6 +3442,7 @@ i915_gem_do_execbuffer(struct drm_device *dev,
 err_vma:
 	if (eb.parser.shadow)
 		intel_gt_buffer_pool_put(eb.parser.shadow->vma->private);
+	i915_acquire_ctx_fini(&eb.acquire);
 	eb_relocs_update_user(&eb);
 	eb_unpin_engine(&eb);
 err_context:
diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_execbuffer.c
index c3b94948ef9f..daedff40236b 100644
--- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_execbuffer.c
@@ -101,11 +101,13 @@ static int __igt_gpu_reloc(struct i915_execbuffer *eb, struct eb_vma *ev)
 		return err;
 	ev->exec->relocation_count = err;
 
+	i915_acquire_ctx_init(&eb->acquire);
+
 	err = eb_reserve_vm(eb);
-	if (err)
-		return err;
+	if (err == 0)
+		err = eb_relocs_gpu(eb);
 
-	err = eb_relocs_gpu(eb);
+	i915_acquire_ctx_fini(&eb->acquire);
 	if (err)
 		return err;
 
-- 
2.20.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [Intel-gfx] [PATCH 19/20] drm/i915/gem: Replace i915_gem_object.mm.mutex with reservation_ww_class
  2020-07-06  6:19 [Intel-gfx] s/obj->mm.lock// Chris Wilson
                   ` (17 preceding siblings ...)
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 18/20] drm/i915/gem: Pull execbuf dma resv under a single critical section Chris Wilson
@ 2020-07-06  6:19 ` Chris Wilson
  2020-07-09 14:06   ` Maarten Lankhorst
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 20/20] drm/i915: Track i915_vma with its own reference counter Chris Wilson
                   ` (5 subsequent siblings)
  24 siblings, 1 reply; 65+ messages in thread
From: Chris Wilson @ 2020-07-06  6:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: Chris Wilson

Our goal is to pull all memory reservations (next iteration
obj->ops->get_pages()) under a ww_mutex, and to align those reservations
with other drivers, i.e. control all such allocations with the
reservation_ww_class. Currently, this is under the purview of the
obj->mm.mutex, and while obj->mm remains an embedded struct we can
"simply" switch to using the reservation_ww_class obj->base.resv->lock

The major consequence is the impact on the shrinker paths as the
reservation_ww_class is used to wrap allocations, and a ww_mutex does
not support subclassing so we cannot do our usual trick of knowing that
we never recurse inside the shrinker and instead have to finish the
reclaim with a trylock. This may result in us failing to release the
pages after having released the vma. This will have to do until a better
idea comes along.

However, this step only converts the mutex over and continues to treat
everything as a single allocation and pinning the pages. With the
ww_mutex in place we can remove the temporary pinning, as we can then
reserve all storage en masse.

One last thing to do: kill the implict page pinning for active vma.
This will require us to invalidate the vma->pages when the backing store
is removed (and we expect that while the vma is active, we mark the
backing store as active so that it cannot be removed while the HW is
busy.)

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 drivers/gpu/drm/i915/gem/i915_gem_clflush.c   |  20 +-
 drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c    |  18 +-
 drivers/gpu/drm/i915/gem/i915_gem_domain.c    |  65 ++---
 .../gpu/drm/i915/gem/i915_gem_execbuffer.c    |  44 +++-
 drivers/gpu/drm/i915/gem/i915_gem_object.c    |   8 +-
 drivers/gpu/drm/i915/gem/i915_gem_object.h    |  37 +--
 .../gpu/drm/i915/gem/i915_gem_object_types.h  |   1 -
 drivers/gpu/drm/i915/gem/i915_gem_pages.c     | 136 +++++------
 drivers/gpu/drm/i915/gem/i915_gem_phys.c      |   8 +-
 drivers/gpu/drm/i915/gem/i915_gem_shrinker.c  |  13 +-
 drivers/gpu/drm/i915/gem/i915_gem_tiling.c    |   2 -
 drivers/gpu/drm/i915/gem/i915_gem_userptr.c   |  15 +-
 .../gpu/drm/i915/gem/selftests/huge_pages.c   |  32 ++-
 .../i915/gem/selftests/i915_gem_coherency.c   |  14 +-
 .../drm/i915/gem/selftests/i915_gem_context.c |  10 +-
 .../drm/i915/gem/selftests/i915_gem_mman.c    |   2 +
 drivers/gpu/drm/i915/gt/gen6_ppgtt.c          |   2 -
 drivers/gpu/drm/i915/gt/gen8_ppgtt.c          |   1 -
 drivers/gpu/drm/i915/gt/intel_ggtt.c          |   5 +-
 drivers/gpu/drm/i915/gt/intel_gtt.h           |   2 -
 drivers/gpu/drm/i915/gt/intel_ppgtt.c         |   1 +
 drivers/gpu/drm/i915/i915_gem.c               |  16 +-
 drivers/gpu/drm/i915/i915_vma.c               | 225 +++++++-----------
 drivers/gpu/drm/i915/i915_vma_types.h         |   6 -
 drivers/gpu/drm/i915/mm/i915_acquire_ctx.c    |  11 +-
 drivers/gpu/drm/i915/selftests/i915_gem_gtt.c |   4 +-
 .../drm/i915/selftests/intel_memory_region.c  |  17 +-
 27 files changed, 319 insertions(+), 396 deletions(-)

diff --git a/drivers/gpu/drm/i915/gem/i915_gem_clflush.c b/drivers/gpu/drm/i915/gem/i915_gem_clflush.c
index bc0223716906..a32fd0d5570b 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_clflush.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_clflush.c
@@ -27,16 +27,8 @@ static void __do_clflush(struct drm_i915_gem_object *obj)
 static int clflush_work(struct dma_fence_work *base)
 {
 	struct clflush *clflush = container_of(base, typeof(*clflush), base);
-	struct drm_i915_gem_object *obj = clflush->obj;
-	int err;
-
-	err = i915_gem_object_pin_pages(obj);
-	if (err)
-		return err;
-
-	__do_clflush(obj);
-	i915_gem_object_unpin_pages(obj);
 
+	__do_clflush(clflush->obj);
 	return 0;
 }
 
@@ -44,7 +36,7 @@ static void clflush_release(struct dma_fence_work *base)
 {
 	struct clflush *clflush = container_of(base, typeof(*clflush), base);
 
-	i915_gem_object_put(clflush->obj);
+	i915_gem_object_unpin_pages(clflush->obj);
 }
 
 static const struct dma_fence_work_ops clflush_ops = {
@@ -63,8 +55,14 @@ static struct clflush *clflush_work_create(struct drm_i915_gem_object *obj)
 	if (!clflush)
 		return NULL;
 
+	if (__i915_gem_object_get_pages_locked(obj)) {
+		kfree(clflush);
+		return NULL;
+	}
+
 	dma_fence_work_init(&clflush->base, &clflush_ops);
-	clflush->obj = i915_gem_object_get(obj); /* obj <-> clflush cycle */
+	__i915_gem_object_pin_pages(obj);
+	clflush->obj = obj; /* Beware the obj.resv <-> clflush fence cycle */
 
 	return clflush;
 }
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c
index 2679380159fc..049a15e6b496 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c
@@ -124,19 +124,12 @@ static int i915_gem_begin_cpu_access(struct dma_buf *dma_buf, enum dma_data_dire
 	bool write = (direction == DMA_BIDIRECTIONAL || direction == DMA_TO_DEVICE);
 	int err;
 
-	err = i915_gem_object_pin_pages(obj);
-	if (err)
-		return err;
-
 	err = i915_gem_object_lock_interruptible(obj);
 	if (err)
-		goto out;
+		return err;
 
 	err = i915_gem_object_set_to_cpu_domain(obj, write);
 	i915_gem_object_unlock(obj);
-
-out:
-	i915_gem_object_unpin_pages(obj);
 	return err;
 }
 
@@ -145,19 +138,12 @@ static int i915_gem_end_cpu_access(struct dma_buf *dma_buf, enum dma_data_direct
 	struct drm_i915_gem_object *obj = dma_buf_to_obj(dma_buf);
 	int err;
 
-	err = i915_gem_object_pin_pages(obj);
-	if (err)
-		return err;
-
 	err = i915_gem_object_lock_interruptible(obj);
 	if (err)
-		goto out;
+		return err;
 
 	err = i915_gem_object_set_to_gtt_domain(obj, false);
 	i915_gem_object_unlock(obj);
-
-out:
-	i915_gem_object_unpin_pages(obj);
 	return err;
 }
 
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_domain.c b/drivers/gpu/drm/i915/gem/i915_gem_domain.c
index 7f76fc68f498..30e4b163588b 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_domain.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_domain.c
@@ -70,7 +70,7 @@ i915_gem_object_set_to_wc_domain(struct drm_i915_gem_object *obj, bool write)
 	 * continue to assume that the obj remained out of the CPU cached
 	 * domain.
 	 */
-	ret = i915_gem_object_pin_pages(obj);
+	ret = __i915_gem_object_get_pages_locked(obj);
 	if (ret)
 		return ret;
 
@@ -94,7 +94,6 @@ i915_gem_object_set_to_wc_domain(struct drm_i915_gem_object *obj, bool write)
 		obj->mm.dirty = true;
 	}
 
-	i915_gem_object_unpin_pages(obj);
 	return 0;
 }
 
@@ -131,7 +130,7 @@ i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write)
 	 * continue to assume that the obj remained out of the CPU cached
 	 * domain.
 	 */
-	ret = i915_gem_object_pin_pages(obj);
+	ret = __i915_gem_object_get_pages_locked(obj);
 	if (ret)
 		return ret;
 
@@ -163,7 +162,6 @@ i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write)
 		spin_unlock(&obj->vma.lock);
 	}
 
-	i915_gem_object_unpin_pages(obj);
 	return 0;
 }
 
@@ -532,13 +530,9 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data,
 	 * continue to assume that the obj remained out of the CPU cached
 	 * domain.
 	 */
-	err = i915_gem_object_pin_pages(obj);
-	if (err)
-		goto out;
-
 	err = i915_gem_object_lock_interruptible(obj);
 	if (err)
-		goto out_unpin;
+		goto out;
 
 	if (read_domains & I915_GEM_DOMAIN_WC)
 		err = i915_gem_object_set_to_wc_domain(obj, write_domain);
@@ -555,8 +549,6 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data,
 	if (write_domain)
 		i915_gem_object_invalidate_frontbuffer(obj, ORIGIN_CPU);
 
-out_unpin:
-	i915_gem_object_unpin_pages(obj);
 out:
 	i915_gem_object_put(obj);
 	return err;
@@ -572,11 +564,13 @@ int i915_gem_object_prepare_read(struct drm_i915_gem_object *obj,
 {
 	int ret;
 
+	assert_object_held(obj);
+
 	*needs_clflush = 0;
 	if (!i915_gem_object_has_struct_page(obj))
 		return -ENODEV;
 
-	ret = i915_gem_object_lock_interruptible(obj);
+	ret = __i915_gem_object_get_pages_locked(obj);
 	if (ret)
 		return ret;
 
@@ -584,19 +578,11 @@ int i915_gem_object_prepare_read(struct drm_i915_gem_object *obj,
 				   I915_WAIT_INTERRUPTIBLE,
 				   MAX_SCHEDULE_TIMEOUT);
 	if (ret)
-		goto err_unlock;
-
-	ret = i915_gem_object_pin_pages(obj);
-	if (ret)
-		goto err_unlock;
+		return ret;
 
 	if (obj->cache_coherent & I915_BO_CACHE_COHERENT_FOR_READ ||
 	    !static_cpu_has(X86_FEATURE_CLFLUSH)) {
-		ret = i915_gem_object_set_to_cpu_domain(obj, false);
-		if (ret)
-			goto err_unpin;
-		else
-			goto out;
+		return i915_gem_object_set_to_cpu_domain(obj, false);
 	}
 
 	i915_gem_object_flush_write_domain(obj, ~I915_GEM_DOMAIN_CPU);
@@ -610,15 +596,7 @@ int i915_gem_object_prepare_read(struct drm_i915_gem_object *obj,
 	    !(obj->read_domains & I915_GEM_DOMAIN_CPU))
 		*needs_clflush = CLFLUSH_BEFORE;
 
-out:
-	/* return with the pages pinned */
 	return 0;
-
-err_unpin:
-	i915_gem_object_unpin_pages(obj);
-err_unlock:
-	i915_gem_object_unlock(obj);
-	return ret;
 }
 
 int i915_gem_object_prepare_write(struct drm_i915_gem_object *obj,
@@ -626,11 +604,13 @@ int i915_gem_object_prepare_write(struct drm_i915_gem_object *obj,
 {
 	int ret;
 
+	assert_object_held(obj);
+
 	*needs_clflush = 0;
 	if (!i915_gem_object_has_struct_page(obj))
 		return -ENODEV;
 
-	ret = i915_gem_object_lock_interruptible(obj);
+	ret = __i915_gem_object_get_pages_locked(obj);
 	if (ret)
 		return ret;
 
@@ -639,20 +619,11 @@ int i915_gem_object_prepare_write(struct drm_i915_gem_object *obj,
 				   I915_WAIT_ALL,
 				   MAX_SCHEDULE_TIMEOUT);
 	if (ret)
-		goto err_unlock;
-
-	ret = i915_gem_object_pin_pages(obj);
-	if (ret)
-		goto err_unlock;
+		return ret;
 
 	if (obj->cache_coherent & I915_BO_CACHE_COHERENT_FOR_WRITE ||
-	    !static_cpu_has(X86_FEATURE_CLFLUSH)) {
-		ret = i915_gem_object_set_to_cpu_domain(obj, true);
-		if (ret)
-			goto err_unpin;
-		else
-			goto out;
-	}
+	    !static_cpu_has(X86_FEATURE_CLFLUSH))
+		return i915_gem_object_set_to_cpu_domain(obj, true);
 
 	i915_gem_object_flush_write_domain(obj, ~I915_GEM_DOMAIN_CPU);
 
@@ -672,15 +643,7 @@ int i915_gem_object_prepare_write(struct drm_i915_gem_object *obj,
 			*needs_clflush |= CLFLUSH_BEFORE;
 	}
 
-out:
 	i915_gem_object_invalidate_frontbuffer(obj, ORIGIN_CPU);
 	obj->mm.dirty = true;
-	/* return with the pages pinned */
 	return 0;
-
-err_unpin:
-	i915_gem_object_unpin_pages(obj);
-err_unlock:
-	i915_gem_object_unlock(obj);
-	return ret;
 }
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
index bcd100f8a6c7..37c0d5058891 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
@@ -963,6 +963,13 @@ static int best_hole(struct drm_mm *mm, struct drm_mm_node *node,
 	} while (1);
 }
 
+static void eb_pin_vma_pages(struct i915_vma *vma, unsigned int count)
+{
+	count = hweight32(count);
+	while (count--)
+		__i915_gem_object_pin_pages(vma->obj);
+}
+
 static int eb_reserve_vma(struct eb_vm_work *work, struct eb_bind_vma *bind)
 {
 	struct drm_i915_gem_exec_object2 *entry = bind->ev->exec;
@@ -1074,7 +1081,6 @@ static int eb_reserve_vma(struct eb_vm_work *work, struct eb_bind_vma *bind)
 		if (unlikely(err))
 			return err;
 
-		atomic_add(I915_VMA_PAGES_ACTIVE, &vma->pages_count);
 		atomic_or(bind_flags, &vma->flags);
 
 		if (i915_vma_is_ggtt(vma))
@@ -1181,9 +1187,14 @@ static void __eb_bind_vma(struct eb_vm_work *work)
 		GEM_BUG_ON(vma->vm != vm);
 		GEM_BUG_ON(!i915_vma_is_active(vma));
 
+		if (!vma->pages)
+			vma->ops->set_pages(vma); /* plain assignment */
+
 		vma->ops->bind_vma(vm, &work->stash, vma,
 				   vma->obj->cache_level, bind->bind_flags);
 
+		eb_pin_vma_pages(vma, bind->bind_flags);
+
 		if (drm_mm_node_allocated(&bind->hole)) {
 			mutex_lock(&vm->mutex);
 			GEM_BUG_ON(bind->hole.mm != &vm->mm);
@@ -1200,7 +1211,6 @@ static void __eb_bind_vma(struct eb_vm_work *work)
 
 put:
 		GEM_BUG_ON(drm_mm_node_allocated(&bind->hole));
-		i915_vma_put_pages(vma);
 	}
 	work->count = 0;
 }
@@ -1302,7 +1312,6 @@ static int eb_prepare_vma(struct eb_vm_work *work,
 	struct eb_bind_vma *bind = &work->bind[idx];
 	struct i915_vma *vma = ev->vma;
 	u64 max_size;
-	int err;
 
 	bind->ev = ev;
 	bind->hole.flags = 0;
@@ -1313,11 +1322,24 @@ static int eb_prepare_vma(struct eb_vm_work *work,
 	if (ev->flags & __EXEC_OBJECT_NEEDS_MAP)
 		max_size = max_t(u64, max_size, vma->fence_size);
 
-	err = i915_vm_alloc_pt_stash(work->vm, &work->stash, max_size);
-	if (err)
-		return err;
+	return i915_vm_alloc_pt_stash(work->vm, &work->stash, max_size);
+}
 
-	return i915_vma_get_pages(vma);
+static int eb_lock_pt(struct i915_execbuffer *eb,
+		      struct i915_vm_pt_stash *stash)
+{
+	struct i915_page_table *pt;
+	int n, err;
+
+	for (n = 0; n < ARRAY_SIZE(stash->pt); n++) {
+		for (pt = stash->pt[n]; pt; pt = pt->stash) {
+			err = i915_acquire_ctx_lock(&eb->acquire, pt->base);
+			if (err)
+				return err;
+		}
+	}
+
+	return 0;
 }
 
 static int wait_for_unbinds(struct i915_execbuffer *eb,
@@ -1440,11 +1462,11 @@ static int eb_reserve_vm(struct i915_execbuffer *eb)
 			}
 		}
 
-		err = eb_acquire_mm(eb);
+		err = eb_lock_pt(eb, &work->stash);
 		if (err)
 			return eb_vm_work_cancel(work, err);
 
-		err = i915_vm_pin_pt_stash(work->vm, &work->stash);
+		err = eb_acquire_mm(eb);
 		if (err)
 			return eb_vm_work_cancel(work, err);
 
@@ -2657,7 +2679,7 @@ static int eb_parse_pipeline(struct i915_execbuffer *eb,
 	if (!pw)
 		return -ENOMEM;
 
-	ptr = i915_gem_object_pin_map(shadow->obj, I915_MAP_FORCE_WB);
+	ptr = __i915_gem_object_pin_map_locked(shadow->obj, I915_MAP_FORCE_WB);
 	if (IS_ERR(ptr)) {
 		err = PTR_ERR(ptr);
 		goto err_free;
@@ -2665,7 +2687,7 @@ static int eb_parse_pipeline(struct i915_execbuffer *eb,
 
 	if (!(batch->obj->cache_coherent & I915_BO_CACHE_COHERENT_FOR_READ) &&
 	    i915_has_memcpy_from_wc()) {
-		ptr = i915_gem_object_pin_map(batch->obj, I915_MAP_WC);
+		ptr = __i915_gem_object_pin_map_locked(batch->obj, I915_MAP_WC);
 		if (IS_ERR(ptr)) {
 			err = PTR_ERR(ptr);
 			goto err_dst;
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.c b/drivers/gpu/drm/i915/gem/i915_gem_object.c
index c8421fd9d2dc..799ad4e648aa 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_object.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_object.c
@@ -53,8 +53,6 @@ void i915_gem_object_init(struct drm_i915_gem_object *obj,
 			  const struct drm_i915_gem_object_ops *ops,
 			  struct lock_class_key *key)
 {
-	__mutex_init(&obj->mm.lock, ops->name ?: "obj->mm.lock", key);
-
 	spin_lock_init(&obj->vma.lock);
 	INIT_LIST_HEAD(&obj->vma.list);
 
@@ -73,10 +71,6 @@ void i915_gem_object_init(struct drm_i915_gem_object *obj,
 	obj->mm.madv = I915_MADV_WILLNEED;
 	INIT_RADIX_TREE(&obj->mm.get_page.radix, GFP_KERNEL | __GFP_NOWARN);
 	mutex_init(&obj->mm.get_page.lock);
-
-	if (IS_ENABLED(CONFIG_LOCKDEP) && i915_gem_object_is_shrinkable(obj))
-		i915_gem_shrinker_taints_mutex(to_i915(obj->base.dev),
-					       &obj->mm.lock);
 }
 
 /**
@@ -229,10 +223,12 @@ static void __i915_gem_free_objects(struct drm_i915_private *i915,
 
 		GEM_BUG_ON(!list_empty(&obj->lut_list));
 
+		i915_gem_object_lock(obj);
 		atomic_set(&obj->mm.pages_pin_count, 0);
 		__i915_gem_object_put_pages(obj);
 		GEM_BUG_ON(i915_gem_object_has_pages(obj));
 		bitmap_free(obj->bit_17);
+		i915_gem_object_unlock(obj);
 
 		if (obj->base.import_attach)
 			drm_prime_gem_destroy(&obj->base, NULL);
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.h b/drivers/gpu/drm/i915/gem/i915_gem_object.h
index 25714bf70b6a..8f1d20f6d42a 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_object.h
+++ b/drivers/gpu/drm/i915/gem/i915_gem_object.h
@@ -275,36 +275,9 @@ void __i915_gem_object_set_pages(struct drm_i915_gem_object *obj,
 				 struct sg_table *pages,
 				 unsigned int sg_page_sizes);
 
-int ____i915_gem_object_get_pages(struct drm_i915_gem_object *obj);
-int __i915_gem_object_get_pages(struct drm_i915_gem_object *obj);
-
-enum i915_mm_subclass { /* lockdep subclass for obj->mm.lock/struct_mutex */
-	I915_MM_NORMAL = 0,
-	/*
-	 * Only used by struct_mutex, when called "recursively" from
-	 * direct-reclaim-esque. Safe because there is only every one
-	 * struct_mutex in the entire system.
-	 */
-	I915_MM_SHRINKER = 1,
-	/*
-	 * Used for obj->mm.lock when allocating pages. Safe because the object
-	 * isn't yet on any LRU, and therefore the shrinker can't deadlock on
-	 * it. As soon as the object has pages, obj->mm.lock nests within
-	 * fs_reclaim.
-	 */
-	I915_MM_GET_PAGES = 1,
-};
-
-static inline int __must_check
-i915_gem_object_pin_pages(struct drm_i915_gem_object *obj)
-{
-	might_lock_nested(&obj->mm.lock, I915_MM_GET_PAGES);
+int __i915_gem_object_get_pages_locked(struct drm_i915_gem_object *obj);
 
-	if (atomic_inc_not_zero(&obj->mm.pages_pin_count))
-		return 0;
-
-	return __i915_gem_object_get_pages(obj);
-}
+int i915_gem_object_pin_pages(struct drm_i915_gem_object *obj);
 
 static inline bool
 i915_gem_object_has_pages(struct drm_i915_gem_object *obj)
@@ -372,6 +345,9 @@ enum i915_map_type {
 void *__must_check i915_gem_object_pin_map(struct drm_i915_gem_object *obj,
 					   enum i915_map_type type);
 
+void *__i915_gem_object_pin_map_locked(struct drm_i915_gem_object *obj,
+				       enum i915_map_type type);
+
 static inline void *__i915_gem_object_mapping(struct drm_i915_gem_object *obj)
 {
 	return page_mask_bits(obj->mm.mapping);
@@ -419,8 +395,7 @@ int i915_gem_object_prepare_write(struct drm_i915_gem_object *obj,
 static inline void
 i915_gem_object_finish_access(struct drm_i915_gem_object *obj)
 {
-	i915_gem_object_unpin_pages(obj);
-	i915_gem_object_unlock(obj);
+	assert_object_held(obj);
 }
 
 static inline struct intel_engine_cs *
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object_types.h b/drivers/gpu/drm/i915/gem/i915_gem_object_types.h
index d0847d7896f9..ae3303ba272c 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_object_types.h
+++ b/drivers/gpu/drm/i915/gem/i915_gem_object_types.h
@@ -187,7 +187,6 @@ struct drm_i915_gem_object {
 		 * Protects the pages and their use. Do not use directly, but
 		 * instead go through the pin/unpin interfaces.
 		 */
-		struct mutex lock;
 		atomic_t pages_pin_count;
 		atomic_t shrink_pin;
 
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_pages.c b/drivers/gpu/drm/i915/gem/i915_gem_pages.c
index af9e48ee4a33..7799f36b4b5d 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_pages.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_pages.c
@@ -18,7 +18,7 @@ void __i915_gem_object_set_pages(struct drm_i915_gem_object *obj,
 	unsigned long supported = INTEL_INFO(i915)->page_sizes;
 	int i;
 
-	lockdep_assert_held(&obj->mm.lock);
+	assert_object_held(obj);
 
 	if (i915_gem_object_is_volatile(obj))
 		obj->mm.madv = I915_MADV_DONTNEED;
@@ -81,13 +81,19 @@ void __i915_gem_object_set_pages(struct drm_i915_gem_object *obj,
 	}
 }
 
-int ____i915_gem_object_get_pages(struct drm_i915_gem_object *obj)
+int __i915_gem_object_get_pages_locked(struct drm_i915_gem_object *obj)
 {
-	struct drm_i915_private *i915 = to_i915(obj->base.dev);
 	int err;
 
+	assert_object_held(obj);
+
+	if (i915_gem_object_has_pages(obj))
+		return 0;
+
+	GEM_BUG_ON(i915_gem_object_has_pinned_pages(obj));
+
 	if (unlikely(obj->mm.madv != I915_MADV_WILLNEED)) {
-		drm_dbg(&i915->drm,
+		drm_dbg(obj->base.dev,
 			"Attempting to obtain a purgeable object\n");
 		return -EFAULT;
 	}
@@ -98,34 +104,33 @@ int ____i915_gem_object_get_pages(struct drm_i915_gem_object *obj)
 	return err;
 }
 
-/* Ensure that the associated pages are gathered from the backing storage
+/*
+ * Ensure that the associated pages are gathered from the backing storage
  * and pinned into our object. i915_gem_object_pin_pages() may be called
  * multiple times before they are released by a single call to
  * i915_gem_object_unpin_pages() - once the pages are no longer referenced
  * either as a result of memory pressure (reaping pages under the shrinker)
  * or as the object is itself released.
  */
-int __i915_gem_object_get_pages(struct drm_i915_gem_object *obj)
+int i915_gem_object_pin_pages(struct drm_i915_gem_object *obj)
 {
 	int err;
 
-	err = mutex_lock_interruptible_nested(&obj->mm.lock, I915_MM_GET_PAGES);
+	might_lock(&obj->base.resv->lock.base);
+
+	if (atomic_inc_not_zero(&obj->mm.pages_pin_count))
+		return 0;
+
+	err = i915_gem_object_lock_interruptible(obj);
 	if (err)
 		return err;
 
-	if (unlikely(!i915_gem_object_has_pages(obj))) {
-		GEM_BUG_ON(i915_gem_object_has_pinned_pages(obj));
-
-		err = ____i915_gem_object_get_pages(obj);
-		if (err)
-			goto unlock;
+	err = __i915_gem_object_get_pages_locked(obj);
+	if (err == 0)
+		atomic_inc(&obj->mm.pages_pin_count);
 
-		smp_mb__before_atomic();
-	}
-	atomic_inc(&obj->mm.pages_pin_count);
+	i915_gem_object_unlock(obj);
 
-unlock:
-	mutex_unlock(&obj->mm.lock);
 	return err;
 }
 
@@ -140,7 +145,7 @@ void i915_gem_object_truncate(struct drm_i915_gem_object *obj)
 /* Try to discard unwanted pages */
 void i915_gem_object_writeback(struct drm_i915_gem_object *obj)
 {
-	lockdep_assert_held(&obj->mm.lock);
+	assert_object_held(obj);
 	GEM_BUG_ON(i915_gem_object_has_pages(obj));
 
 	if (obj->ops->writeback)
@@ -194,17 +199,15 @@ __i915_gem_object_unset_pages(struct drm_i915_gem_object *obj)
 int __i915_gem_object_put_pages(struct drm_i915_gem_object *obj)
 {
 	struct sg_table *pages;
-	int err;
+
+	/* May be called by shrinker from within get_pages() (on another bo) */
+	assert_object_held(obj);
 
 	if (i915_gem_object_has_pinned_pages(obj))
 		return -EBUSY;
 
-	/* May be called by shrinker from within get_pages() (on another bo) */
-	mutex_lock(&obj->mm.lock);
-	if (unlikely(atomic_read(&obj->mm.pages_pin_count))) {
-		err = -EBUSY;
-		goto unlock;
-	}
+	if (unlikely(atomic_read(&obj->mm.pages_pin_count)))
+		return -EBUSY;
 
 	i915_gem_object_release_mmap_offset(obj);
 
@@ -227,11 +230,7 @@ int __i915_gem_object_put_pages(struct drm_i915_gem_object *obj)
 	if (!IS_ERR(pages))
 		obj->ops->put_pages(obj, pages);
 
-	err = 0;
-unlock:
-	mutex_unlock(&obj->mm.lock);
-
-	return err;
+	return 0;
 }
 
 static inline pte_t iomap_pte(resource_size_t base,
@@ -311,48 +310,28 @@ static void *i915_gem_object_map(struct drm_i915_gem_object *obj,
 	return area->addr;
 }
 
-/* get, pin, and map the pages of the object into kernel space */
-void *i915_gem_object_pin_map(struct drm_i915_gem_object *obj,
-			      enum i915_map_type type)
+void *__i915_gem_object_pin_map_locked(struct drm_i915_gem_object *obj,
+				       enum i915_map_type type)
 {
 	enum i915_map_type has_type;
 	unsigned int flags;
 	bool pinned;
 	void *ptr;
-	int err;
+
+	assert_object_held(obj);
+	GEM_BUG_ON(!i915_gem_object_has_pages(obj));
 
 	flags = I915_GEM_OBJECT_HAS_STRUCT_PAGE | I915_GEM_OBJECT_HAS_IOMEM;
 	if (!i915_gem_object_type_has(obj, flags))
 		return ERR_PTR(-ENXIO);
 
-	err = mutex_lock_interruptible_nested(&obj->mm.lock, I915_MM_GET_PAGES);
-	if (err)
-		return ERR_PTR(err);
-
 	pinned = !(type & I915_MAP_OVERRIDE);
 	type &= ~I915_MAP_OVERRIDE;
 
-	if (!atomic_inc_not_zero(&obj->mm.pages_pin_count)) {
-		if (unlikely(!i915_gem_object_has_pages(obj))) {
-			GEM_BUG_ON(i915_gem_object_has_pinned_pages(obj));
-
-			err = ____i915_gem_object_get_pages(obj);
-			if (err)
-				goto err_unlock;
-
-			smp_mb__before_atomic();
-		}
-		atomic_inc(&obj->mm.pages_pin_count);
-		pinned = false;
-	}
-	GEM_BUG_ON(!i915_gem_object_has_pages(obj));
-
 	ptr = page_unpack_bits(obj->mm.mapping, &has_type);
 	if (ptr && has_type != type) {
-		if (pinned) {
-			err = -EBUSY;
-			goto err_unpin;
-		}
+		if (pinned)
+			return ERR_PTR(-EBUSY);
 
 		unmap_object(obj, ptr);
 
@@ -361,23 +340,38 @@ void *i915_gem_object_pin_map(struct drm_i915_gem_object *obj,
 
 	if (!ptr) {
 		ptr = i915_gem_object_map(obj, type);
-		if (!ptr) {
-			err = -ENOMEM;
-			goto err_unpin;
-		}
+		if (!ptr)
+			return ERR_PTR(-ENOMEM);
 
 		obj->mm.mapping = page_pack_bits(ptr, type);
 	}
 
-out_unlock:
-	mutex_unlock(&obj->mm.lock);
+	__i915_gem_object_pin_pages(obj);
 	return ptr;
+}
+
+/* get, pin, and map the pages of the object into kernel space */
+void *i915_gem_object_pin_map(struct drm_i915_gem_object *obj,
+			      enum i915_map_type type)
+{
+	void *ptr;
+	int err;
+
+	err = i915_gem_object_lock_interruptible(obj);
+	if (err)
+		return ERR_PTR(err);
 
-err_unpin:
-	atomic_dec(&obj->mm.pages_pin_count);
-err_unlock:
-	ptr = ERR_PTR(err);
-	goto out_unlock;
+	err = __i915_gem_object_get_pages_locked(obj);
+	if (err) {
+		ptr = ERR_PTR(err);
+		goto out;
+	}
+
+	ptr = __i915_gem_object_pin_map_locked(obj, type);
+
+out:
+	i915_gem_object_unlock(obj);
+	return ptr;
 }
 
 void __i915_gem_object_flush_map(struct drm_i915_gem_object *obj,
@@ -419,7 +413,9 @@ i915_gem_object_get_sg(struct drm_i915_gem_object *obj,
 
 	might_sleep();
 	GEM_BUG_ON(n >= obj->base.size >> PAGE_SHIFT);
-	GEM_BUG_ON(!i915_gem_object_has_pinned_pages(obj));
+	GEM_BUG_ON(!i915_gem_object_has_pages(obj));
+	GEM_BUG_ON(!mutex_is_locked(&obj->base.resv->lock.base) &&
+		   !i915_gem_object_has_pinned_pages(obj));
 
 	/* As we iterate forward through the sg, we record each entry in a
 	 * radixtree for quick repeated (backwards) lookups. If we have seen
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_phys.c b/drivers/gpu/drm/i915/gem/i915_gem_phys.c
index 28147aab47b9..f7f93b68b7c1 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_phys.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_phys.c
@@ -165,7 +165,7 @@ int i915_gem_object_attach_phys(struct drm_i915_gem_object *obj, int align)
 	if (err)
 		return err;
 
-	mutex_lock_nested(&obj->mm.lock, I915_MM_GET_PAGES);
+	i915_gem_object_lock(obj);
 
 	if (obj->mm.madv != I915_MADV_WILLNEED) {
 		err = -EFAULT;
@@ -186,7 +186,7 @@ int i915_gem_object_attach_phys(struct drm_i915_gem_object *obj, int align)
 
 	obj->ops = &i915_gem_phys_ops;
 
-	err = ____i915_gem_object_get_pages(obj);
+	err = __i915_gem_object_get_pages_locked(obj);
 	if (err)
 		goto err_xfer;
 
@@ -198,7 +198,7 @@ int i915_gem_object_attach_phys(struct drm_i915_gem_object *obj, int align)
 
 	i915_gem_object_release_memory_region(obj);
 
-	mutex_unlock(&obj->mm.lock);
+	i915_gem_object_unlock(obj);
 	return 0;
 
 err_xfer:
@@ -209,7 +209,7 @@ int i915_gem_object_attach_phys(struct drm_i915_gem_object *obj, int align)
 		__i915_gem_object_set_pages(obj, pages, sg_page_sizes);
 	}
 err_unlock:
-	mutex_unlock(&obj->mm.lock);
+	i915_gem_object_unlock(obj);
 	return err;
 }
 
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_shrinker.c b/drivers/gpu/drm/i915/gem/i915_gem_shrinker.c
index 1ced1e5d2ec0..a2713b251d70 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_shrinker.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_shrinker.c
@@ -45,10 +45,7 @@ static bool unsafe_drop_pages(struct drm_i915_gem_object *obj,
 	if (!(shrink & I915_SHRINK_BOUND))
 		flags = I915_GEM_OBJECT_UNBIND_TEST;
 
-	if (i915_gem_object_unbind(obj, flags) == 0)
-		__i915_gem_object_put_pages(obj);
-
-	return !i915_gem_object_has_pages(obj);
+	return i915_gem_object_unbind(obj, flags) == 0;
 }
 
 static void try_to_writeback(struct drm_i915_gem_object *obj,
@@ -192,14 +189,14 @@ i915_gem_shrink(struct drm_i915_private *i915,
 
 			spin_unlock_irqrestore(&i915->mm.obj_lock, flags);
 
-			if (unsafe_drop_pages(obj, shrink)) {
-				/* May arrive from get_pages on another bo */
-				mutex_lock(&obj->mm.lock);
+			if (unsafe_drop_pages(obj, shrink) &&
+			    i915_gem_object_trylock(obj)) {
+				__i915_gem_object_put_pages(obj);
 				if (!i915_gem_object_has_pages(obj)) {
 					try_to_writeback(obj, shrink);
 					count += obj->base.size >> PAGE_SHIFT;
 				}
-				mutex_unlock(&obj->mm.lock);
+				i915_gem_object_unlock(obj);
 			}
 
 			scanned += obj->base.size >> PAGE_SHIFT;
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_tiling.c b/drivers/gpu/drm/i915/gem/i915_gem_tiling.c
index ff72ee2fd9cd..ac12e1c20e66 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_tiling.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_tiling.c
@@ -265,7 +265,6 @@ i915_gem_object_set_tiling(struct drm_i915_gem_object *obj,
 	 * pages to prevent them being swapped out and causing corruption
 	 * due to the change in swizzling.
 	 */
-	mutex_lock(&obj->mm.lock);
 	if (i915_gem_object_has_pages(obj) &&
 	    obj->mm.madv == I915_MADV_WILLNEED &&
 	    i915->quirks & QUIRK_PIN_SWIZZLED_PAGES) {
@@ -280,7 +279,6 @@ i915_gem_object_set_tiling(struct drm_i915_gem_object *obj,
 			obj->mm.quirked = true;
 		}
 	}
-	mutex_unlock(&obj->mm.lock);
 
 	spin_lock(&obj->vma.lock);
 	for_each_ggtt_vma(vma, obj) {
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_userptr.c b/drivers/gpu/drm/i915/gem/i915_gem_userptr.c
index e946032b13e4..80907c00c6fd 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_userptr.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_userptr.c
@@ -129,8 +129,15 @@ userptr_mn_invalidate_range_start(struct mmu_notifier *_mn,
 		ret = i915_gem_object_unbind(obj,
 					     I915_GEM_OBJECT_UNBIND_ACTIVE |
 					     I915_GEM_OBJECT_UNBIND_BARRIER);
-		if (ret == 0)
-			ret = __i915_gem_object_put_pages(obj);
+		if (ret == 0) {
+			/* ww_mutex and mmu_notifier is fs_reclaim tainted */
+			if (i915_gem_object_trylock(obj)) {
+				ret = __i915_gem_object_put_pages(obj);
+				i915_gem_object_unlock(obj);
+			} else {
+				ret = -EAGAIN;
+			}
+		}
 		i915_gem_object_put(obj);
 		if (ret)
 			return ret;
@@ -485,7 +492,7 @@ __i915_gem_userptr_get_pages_worker(struct work_struct *_work)
 		}
 	}
 
-	mutex_lock_nested(&obj->mm.lock, I915_MM_GET_PAGES);
+	i915_gem_object_lock(obj);
 	if (obj->userptr.work == &work->work) {
 		struct sg_table *pages = ERR_PTR(ret);
 
@@ -502,7 +509,7 @@ __i915_gem_userptr_get_pages_worker(struct work_struct *_work)
 		if (IS_ERR(pages))
 			__i915_gem_userptr_set_active(obj, false);
 	}
-	mutex_unlock(&obj->mm.lock);
+	i915_gem_object_unlock(obj);
 
 	unpin_user_pages(pvec, pinned);
 	kvfree(pvec);
diff --git a/drivers/gpu/drm/i915/gem/selftests/huge_pages.c b/drivers/gpu/drm/i915/gem/selftests/huge_pages.c
index 9fb06fcc8f8f..a16aae4acf44 100644
--- a/drivers/gpu/drm/i915/gem/selftests/huge_pages.c
+++ b/drivers/gpu/drm/i915/gem/selftests/huge_pages.c
@@ -452,6 +452,15 @@ static int igt_mock_exhaust_device_supported_pages(void *arg)
 	return err;
 }
 
+static void close_object(struct drm_i915_gem_object *obj)
+{
+	i915_gem_object_lock(obj);
+	__i915_gem_object_put_pages(obj);
+	i915_gem_object_unlock(obj);
+
+	i915_gem_object_put(obj);
+}
+
 static int igt_mock_memory_region_huge_pages(void *arg)
 {
 	const unsigned int flags[] = { 0, I915_BO_ALLOC_CONTIGUOUS };
@@ -514,8 +523,7 @@ static int igt_mock_memory_region_huge_pages(void *arg)
 			}
 
 			i915_vma_unpin(vma);
-			__i915_gem_object_put_pages(obj);
-			i915_gem_object_put(obj);
+			close_object(obj);
 		}
 	}
 
@@ -633,8 +641,7 @@ static int igt_mock_ppgtt_misaligned_dma(void *arg)
 		}
 
 		i915_gem_object_unpin_pages(obj);
-		__i915_gem_object_put_pages(obj);
-		i915_gem_object_put(obj);
+		close_object(obj);
 	}
 
 	return 0;
@@ -655,8 +662,7 @@ static void close_object_list(struct list_head *objects,
 	list_for_each_entry_safe(obj, on, objects, st_link) {
 		list_del(&obj->st_link);
 		i915_gem_object_unpin_pages(obj);
-		__i915_gem_object_put_pages(obj);
-		i915_gem_object_put(obj);
+		close_object(obj);
 	}
 }
 
@@ -923,8 +929,7 @@ static int igt_mock_ppgtt_64K(void *arg)
 
 			i915_vma_unpin(vma);
 			i915_gem_object_unpin_pages(obj);
-			__i915_gem_object_put_pages(obj);
-			i915_gem_object_put(obj);
+			close_object(obj);
 		}
 	}
 
@@ -964,9 +969,10 @@ __cpu_check_shmem(struct drm_i915_gem_object *obj, u32 dword, u32 val)
 	unsigned long n;
 	int err;
 
+	i915_gem_object_lock(obj);
 	err = i915_gem_object_prepare_read(obj, &needs_flush);
 	if (err)
-		return err;
+		goto unlock;
 
 	for (n = 0; n < obj->base.size >> PAGE_SHIFT; ++n) {
 		u32 *ptr = kmap_atomic(i915_gem_object_get_page(obj, n));
@@ -986,7 +992,8 @@ __cpu_check_shmem(struct drm_i915_gem_object *obj, u32 dword, u32 val)
 	}
 
 	i915_gem_object_finish_access(obj);
-
+unlock:
+	i915_gem_object_unlock(obj);
 	return err;
 }
 
@@ -1304,7 +1311,9 @@ static int igt_ppgtt_smoke_huge(void *arg)
 		}
 out_unpin:
 		i915_gem_object_unpin_pages(obj);
+		i915_gem_object_lock(obj);
 		__i915_gem_object_put_pages(obj);
+		i915_gem_object_unlock(obj);
 out_put:
 		i915_gem_object_put(obj);
 
@@ -1392,8 +1401,7 @@ static int igt_ppgtt_sanity_check(void *arg)
 			err = igt_write_huge(ctx, obj);
 
 			i915_gem_object_unpin_pages(obj);
-			__i915_gem_object_put_pages(obj);
-			i915_gem_object_put(obj);
+			close_object(obj);
 
 			if (err) {
 				pr_err("%s write-huge failed with size=%u pages=%u i=%d, j=%d\n",
diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_coherency.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_coherency.c
index 87d7d8aa080f..b8dd6fabe70a 100644
--- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_coherency.c
+++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_coherency.c
@@ -27,9 +27,10 @@ static int cpu_set(struct context *ctx, unsigned long offset, u32 v)
 	u32 *cpu;
 	int err;
 
+	i915_gem_object_lock(ctx->obj);
 	err = i915_gem_object_prepare_write(ctx->obj, &needs_clflush);
 	if (err)
-		return err;
+		goto unlock;
 
 	page = i915_gem_object_get_page(ctx->obj, offset >> PAGE_SHIFT);
 	map = kmap_atomic(page);
@@ -46,7 +47,9 @@ static int cpu_set(struct context *ctx, unsigned long offset, u32 v)
 	kunmap_atomic(map);
 	i915_gem_object_finish_access(ctx->obj);
 
-	return 0;
+unlock:
+	i915_gem_object_unlock(ctx->obj);
+	return err;
 }
 
 static int cpu_get(struct context *ctx, unsigned long offset, u32 *v)
@@ -57,9 +60,10 @@ static int cpu_get(struct context *ctx, unsigned long offset, u32 *v)
 	u32 *cpu;
 	int err;
 
+	i915_gem_object_lock(ctx->obj);
 	err = i915_gem_object_prepare_read(ctx->obj, &needs_clflush);
 	if (err)
-		return err;
+		goto unlock;
 
 	page = i915_gem_object_get_page(ctx->obj, offset >> PAGE_SHIFT);
 	map = kmap_atomic(page);
@@ -73,7 +77,9 @@ static int cpu_get(struct context *ctx, unsigned long offset, u32 *v)
 	kunmap_atomic(map);
 	i915_gem_object_finish_access(ctx->obj);
 
-	return 0;
+unlock:
+	i915_gem_object_unlock(ctx->obj);
+	return err;
 }
 
 static int gtt_set(struct context *ctx, unsigned long offset, u32 v)
diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c
index 1308198543d8..c5d06af890bc 100644
--- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c
+++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c
@@ -461,9 +461,10 @@ static int cpu_fill(struct drm_i915_gem_object *obj, u32 value)
 	unsigned int n, m, need_flush;
 	int err;
 
+	i915_gem_object_lock(obj);
 	err = i915_gem_object_prepare_write(obj, &need_flush);
 	if (err)
-		return err;
+		goto unlock;
 
 	for (n = 0; n < real_page_count(obj); n++) {
 		u32 *map;
@@ -479,6 +480,8 @@ static int cpu_fill(struct drm_i915_gem_object *obj, u32 value)
 	i915_gem_object_finish_access(obj);
 	obj->read_domains = I915_GEM_DOMAIN_GTT | I915_GEM_DOMAIN_CPU;
 	obj->write_domain = 0;
+unlock:
+	i915_gem_object_unlock(obj);
 	return 0;
 }
 
@@ -488,9 +491,10 @@ static noinline int cpu_check(struct drm_i915_gem_object *obj,
 	unsigned int n, m, needs_flush;
 	int err;
 
+	i915_gem_object_lock(obj);
 	err = i915_gem_object_prepare_read(obj, &needs_flush);
 	if (err)
-		return err;
+		goto unlock;
 
 	for (n = 0; n < real_page_count(obj); n++) {
 		u32 *map;
@@ -527,6 +531,8 @@ static noinline int cpu_check(struct drm_i915_gem_object *obj,
 	}
 
 	i915_gem_object_finish_access(obj);
+unlock:
+	i915_gem_object_unlock(obj);
 	return err;
 }
 
diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c
index 9c7402ce5bf9..11f734fea3ab 100644
--- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c
+++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c
@@ -1297,7 +1297,9 @@ static int __igt_mmap_revoke(struct drm_i915_private *i915,
 	}
 
 	if (type != I915_MMAP_TYPE_GTT) {
+		i915_gem_object_lock(obj);
 		__i915_gem_object_put_pages(obj);
+		i915_gem_object_unlock(obj);
 		if (i915_gem_object_has_pages(obj)) {
 			pr_err("Failed to put-pages object!\n");
 			err = -EINVAL;
diff --git a/drivers/gpu/drm/i915/gt/gen6_ppgtt.c b/drivers/gpu/drm/i915/gt/gen6_ppgtt.c
index 1bfc7b9101e7..ea9a8044fd56 100644
--- a/drivers/gpu/drm/i915/gt/gen6_ppgtt.c
+++ b/drivers/gpu/drm/i915/gt/gen6_ppgtt.c
@@ -349,7 +349,6 @@ static struct i915_vma *pd_vma_create(struct gen6_ppgtt *ppgtt, int size)
 	i915_active_init(&vma->active, NULL, NULL);
 
 	kref_init(&vma->ref);
-	mutex_init(&vma->pages_mutex);
 	vma->vm = i915_vm_get(&ggtt->vm);
 	vma->ops = &pd_vma_ops;
 	vma->private = ppgtt;
@@ -436,7 +435,6 @@ struct i915_ppgtt *gen6_ppgtt_create(struct intel_gt *gt)
 	ppgtt_init(&ppgtt->base, gt);
 	ppgtt->base.vm.top = 1;
 
-	ppgtt->base.vm.bind_async_flags = I915_VMA_LOCAL_BIND;
 	ppgtt->base.vm.allocate_va_range = gen6_alloc_va_range;
 	ppgtt->base.vm.clear_range = gen6_ppgtt_clear_range;
 	ppgtt->base.vm.insert_entries = gen6_ppgtt_insert_entries;
diff --git a/drivers/gpu/drm/i915/gt/gen8_ppgtt.c b/drivers/gpu/drm/i915/gt/gen8_ppgtt.c
index f987781382d5..30037a00dad2 100644
--- a/drivers/gpu/drm/i915/gt/gen8_ppgtt.c
+++ b/drivers/gpu/drm/i915/gt/gen8_ppgtt.c
@@ -719,7 +719,6 @@ struct i915_ppgtt *gen8_ppgtt_create(struct intel_gt *gt)
 			goto err_free_pd;
 	}
 
-	ppgtt->vm.bind_async_flags = I915_VMA_LOCAL_BIND;
 	ppgtt->vm.insert_entries = gen8_ppgtt_insert;
 	ppgtt->vm.allocate_va_range = gen8_ppgtt_alloc;
 	ppgtt->vm.clear_range = gen8_ppgtt_clear;
diff --git a/drivers/gpu/drm/i915/gt/intel_ggtt.c b/drivers/gpu/drm/i915/gt/intel_ggtt.c
index 33a3f627ddb1..59a4a3ab6bfd 100644
--- a/drivers/gpu/drm/i915/gt/intel_ggtt.c
+++ b/drivers/gpu/drm/i915/gt/intel_ggtt.c
@@ -628,7 +628,6 @@ static int init_aliasing_ppgtt(struct i915_ggtt *ggtt)
 	ppgtt->vm.allocate_va_range(&ppgtt->vm, &stash, 0, ggtt->vm.total);
 
 	ggtt->alias = ppgtt;
-	ggtt->vm.bind_async_flags |= ppgtt->vm.bind_async_flags;
 
 	GEM_BUG_ON(ggtt->vm.vma_ops.bind_vma != ggtt_bind_vma);
 	ggtt->vm.vma_ops.bind_vma = aliasing_gtt_bind_vma;
@@ -862,8 +861,6 @@ static int gen8_gmch_probe(struct i915_ggtt *ggtt)
 	    IS_CHERRYVIEW(i915) /* fails with concurrent use/update */) {
 		ggtt->vm.insert_entries = bxt_vtd_ggtt_insert_entries__BKL;
 		ggtt->vm.insert_page    = bxt_vtd_ggtt_insert_page__BKL;
-		ggtt->vm.bind_async_flags =
-			I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND;
 	}
 
 	ggtt->invalidate = gen8_ggtt_invalidate;
@@ -1429,7 +1426,7 @@ i915_get_ggtt_vma_pages(struct i915_vma *vma)
 	 * must be the vma->pages. A simple rule is that vma->pages must only
 	 * be accessed when the obj->mm.pages are pinned.
 	 */
-	GEM_BUG_ON(!i915_gem_object_has_pinned_pages(vma->obj));
+	GEM_BUG_ON(!i915_gem_object_has_pages(vma->obj));
 
 	switch (vma->ggtt_view.type) {
 	default:
diff --git a/drivers/gpu/drm/i915/gt/intel_gtt.h b/drivers/gpu/drm/i915/gt/intel_gtt.h
index 7ed804027ba1..a4809f4ffe53 100644
--- a/drivers/gpu/drm/i915/gt/intel_gtt.h
+++ b/drivers/gpu/drm/i915/gt/intel_gtt.h
@@ -226,8 +226,6 @@ struct i915_address_space {
 	u64 total;		/* size addr space maps (ex. 2GB for ggtt) */
 	u64 reserved;		/* size addr space reserved */
 
-	unsigned int bind_async_flags;
-
 	/*
 	 * Each active user context has its own address space (in full-ppgtt).
 	 * Since the vm may be shared between multiple contexts, we count how
diff --git a/drivers/gpu/drm/i915/gt/intel_ppgtt.c b/drivers/gpu/drm/i915/gt/intel_ppgtt.c
index 1215c1d6d9e3..0ac0bc14f1de 100644
--- a/drivers/gpu/drm/i915/gt/intel_ppgtt.c
+++ b/drivers/gpu/drm/i915/gt/intel_ppgtt.c
@@ -268,6 +268,7 @@ void i915_vm_free_pt_stash(struct i915_address_space *vm,
 int ppgtt_set_pages(struct i915_vma *vma)
 {
 	GEM_BUG_ON(vma->pages);
+	GEM_BUG_ON(IS_ERR_OR_NULL(vma->obj->mm.pages));
 
 	vma->pages = vma->obj->mm.pages;
 	vma->page_sizes = vma->obj->mm.page_sizes;
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index e998f25f30a3..0fbe438c4523 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -335,12 +335,16 @@ i915_gem_shmem_pread(struct drm_i915_gem_object *obj,
 	u64 remain;
 	int ret;
 
+	i915_gem_object_lock(obj);
 	ret = i915_gem_object_prepare_read(obj, &needs_clflush);
-	if (ret)
+	if (ret) {
+		i915_gem_object_unlock(obj);
 		return ret;
+	}
 
 	fence = i915_gem_object_lock_fence(obj);
 	i915_gem_object_finish_access(obj);
+	i915_gem_object_unlock(obj);
 	if (!fence)
 		return -ENOMEM;
 
@@ -734,12 +738,16 @@ i915_gem_shmem_pwrite(struct drm_i915_gem_object *obj,
 	u64 remain;
 	int ret;
 
+	i915_gem_object_lock(obj);
 	ret = i915_gem_object_prepare_write(obj, &needs_clflush);
-	if (ret)
+	if (ret) {
+		i915_gem_object_unlock(obj);
 		return ret;
+	}
 
 	fence = i915_gem_object_lock_fence(obj);
 	i915_gem_object_finish_access(obj);
+	i915_gem_object_unlock(obj);
 	if (!fence)
 		return -ENOMEM;
 
@@ -1063,7 +1071,7 @@ i915_gem_madvise_ioctl(struct drm_device *dev, void *data,
 	if (!obj)
 		return -ENOENT;
 
-	err = mutex_lock_interruptible(&obj->mm.lock);
+	err = i915_gem_object_lock_interruptible(obj);
 	if (err)
 		goto out;
 
@@ -1109,7 +1117,7 @@ i915_gem_madvise_ioctl(struct drm_device *dev, void *data,
 		i915_gem_object_truncate(obj);
 
 	args->retained = obj->mm.madv != __I915_MADV_PURGED;
-	mutex_unlock(&obj->mm.lock);
+	i915_gem_object_unlock(obj);
 
 out:
 	i915_gem_object_put(obj);
diff --git a/drivers/gpu/drm/i915/i915_vma.c b/drivers/gpu/drm/i915/i915_vma.c
index cbfb11310846..5623f6e8373d 100644
--- a/drivers/gpu/drm/i915/i915_vma.c
+++ b/drivers/gpu/drm/i915/i915_vma.c
@@ -116,7 +116,6 @@ vma_create(struct drm_i915_gem_object *obj,
 		return ERR_PTR(-ENOMEM);
 
 	kref_init(&vma->ref);
-	mutex_init(&vma->pages_mutex);
 	vma->vm = i915_vm_get(vm);
 	vma->ops = &vm->vma_ops;
 	vma->obj = obj;
@@ -295,16 +294,31 @@ struct i915_vma_work {
 	struct i915_address_space *vm;
 	struct i915_vm_pt_stash stash;
 	struct i915_vma *vma;
-	struct drm_i915_gem_object *pinned;
 	struct i915_sw_dma_fence_cb cb;
 	enum i915_cache_level cache_level;
 	unsigned int flags;
 };
 
+static void pin_pages(struct i915_vma *vma, unsigned int bind)
+{
+	bind = hweight32(bind & I915_VMA_BIND_MASK);
+	while (bind--)
+		__i915_gem_object_pin_pages(vma->obj);
+}
+
 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;
+
+	if (!vma->pages) {
+		err = vma->ops->set_pages(vma);
+		if (err) {
+			atomic_or(I915_VMA_ERROR, &vma->flags);
+			return err;
+		}
+	}
 
 	vma->ops->bind_vma(vw->vm, &vw->stash,
 			   vma, vw->cache_level, vw->flags);
@@ -315,8 +329,8 @@ static void __vma_release(struct dma_fence_work *work)
 {
 	struct i915_vma_work *vw = container_of(work, typeof(*vw), base);
 
-	if (vw->pinned)
-		__i915_gem_object_unpin_pages(vw->pinned);
+	if (work->dma.error && vw->flags)
+		atomic_or(I915_VMA_ERROR, &vw->vma->flags);
 
 	i915_vm_free_pt_stash(vw->vm, &vw->stash);
 	i915_vm_put(vw->vm);
@@ -389,6 +403,7 @@ int i915_vma_bind(struct i915_vma *vma,
 		  u32 flags,
 		  struct i915_vma_work *work)
 {
+	struct dma_fence *prev;
 	u32 bind_flags;
 	u32 vma_flags;
 
@@ -413,41 +428,39 @@ int i915_vma_bind(struct i915_vma *vma,
 	if (bind_flags == 0)
 		return 0;
 
-	GEM_BUG_ON(!vma->pages);
-
 	trace_i915_vma_bind(vma, bind_flags);
-	if (work && bind_flags & vma->vm->bind_async_flags) {
-		struct dma_fence *prev;
 
-		work->vma = vma;
-		work->cache_level = cache_level;
-		work->flags = bind_flags;
+	work->vma = vma;
+	work->cache_level = cache_level;
+	work->flags = bind_flags;
+	work->base.dma.error = 0; /* enable the queue_work() */
 
-		/*
-		 * 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.
-		 */
-		prev = i915_active_set_exclusive(&vma->active, &work->base.dma);
-		if (prev) {
-			__i915_sw_fence_await_dma_fence(&work->base.chain,
-							prev,
-							&work->cb);
-			dma_fence_put(prev);
-		}
-
-		work->base.dma.error = 0; /* enable the queue_work() */
+	/*
+	 * 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.
+	 */
+	prev = i915_active_set_exclusive(&vma->active, &work->base.dma);
+	if (prev) {
+		__i915_sw_fence_await_dma_fence(&work->base.chain,
+						prev,
+						&work->cb);
+		dma_fence_put(prev);
+	}
 
-		if (vma->obj) {
-			__i915_gem_object_pin_pages(vma->obj);
-			work->pinned = vma->obj;
+	if (vma->obj) {
+		if (IS_ERR(vma->obj->mm.pages)) {
+			i915_sw_fence_set_error_once(&work->base.chain,
+						     PTR_ERR(vma->obj->mm.pages));
+			atomic_or(I915_VMA_ERROR, &vma->flags);
+			bind_flags = 0;
 		}
-	} else {
-		vma->ops->bind_vma(vma->vm, NULL, vma, cache_level, bind_flags);
+
+		pin_pages(vma, bind_flags);
 	}
 
 	atomic_or(bind_flags, &vma->flags);
@@ -690,6 +703,9 @@ i915_vma_insert(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
 		if (ret)
 			return ret;
 	} else {
+		const unsigned long page_sizes =
+			INTEL_INFO(vma->vm->i915)->page_sizes;
+
 		/*
 		 * We only support huge gtt pages through the 48b PPGTT,
 		 * however we also don't want to force any alignment for
@@ -699,7 +715,7 @@ i915_vma_insert(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
 		 * forseeable future. See also i915_ggtt_offset().
 		 */
 		if (upper_32_bits(end - 1) &&
-		    vma->page_sizes.sg > I915_GTT_PAGE_SIZE) {
+		    page_sizes > I915_GTT_PAGE_SIZE) {
 			/*
 			 * We can't mix 64K and 4K PTEs in the same page-table
 			 * (2M block), and so to avoid the ugliness and
@@ -707,7 +723,7 @@ i915_vma_insert(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
 			 * objects to 2M.
 			 */
 			u64 page_alignment =
-				rounddown_pow_of_two(vma->page_sizes.sg |
+				rounddown_pow_of_two(page_sizes |
 						     I915_GTT_PAGE_SIZE_2M);
 
 			/*
@@ -719,7 +735,7 @@ i915_vma_insert(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
 
 			alignment = max(alignment, page_alignment);
 
-			if (vma->page_sizes.sg & I915_GTT_PAGE_SIZE_64K)
+			if (page_sizes & I915_GTT_PAGE_SIZE_64K)
 				size = round_up(size, I915_GTT_PAGE_SIZE_2M);
 		}
 
@@ -796,74 +812,6 @@ bool i915_vma_pin_inplace(struct i915_vma *vma, unsigned int flags)
 	return pinned;
 }
 
-int i915_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) {
-			if (vma->obj)
-				i915_gem_object_unpin_pages(vma->obj);
-			goto unlock;
-		}
-	}
-	atomic_inc(&vma->pages_count);
-
-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);
-	}
-	mutex_unlock(&vma->pages_mutex);
-}
-
-void i915_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;
-	if (count)
-		__vma_put_pages(vma, count | count << I915_VMA_PAGES_BIAS);
-}
-
 static int __wait_for_unbind(struct i915_vma *vma, unsigned int flags)
 {
 	return __i915_vma_wait_excl(vma, false, flags);
@@ -874,6 +822,7 @@ int i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
 	struct i915_vma_work *work = NULL;
 	intel_wakeref_t wakeref = 0;
 	unsigned int bound;
+	u64 max_size;
 	int err;
 
 	BUILD_BUG_ON(PIN_GLOBAL != I915_VMA_GLOBAL_BIND);
@@ -885,9 +834,11 @@ int i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
 	if (i915_vma_pin_inplace(vma, flags & I915_VMA_BIND_MASK))
 		return 0;
 
-	err = i915_vma_get_pages(vma);
-	if (err)
-		return err;
+	if (vma->obj) {
+		err = i915_gem_object_pin_pages(vma->obj);
+		if (err)
+			return err;
+	}
 
 	if (flags & PIN_GLOBAL)
 		wakeref = intel_runtime_pm_get(&vma->vm->i915->runtime_pm);
@@ -896,29 +847,25 @@ int i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
 	if (err)
 		goto err_rpm;
 
-	if (flags & vma->vm->bind_async_flags) {
-		u64 max_size;
-
-		work = i915_vma_work();
-		if (!work) {
-			err = -ENOMEM;
-			goto err_rpm;
-		}
+	work = i915_vma_work();
+	if (!work) {
+		err = -ENOMEM;
+		goto err_rpm;
+	}
 
-		work->vm = i915_vm_get(vma->vm);
+	work->vm = i915_vm_get(vma->vm);
 
-		/* Allocate enough page directories to cover worst case */
-		max_size = max(size, vma->size);
-		if (flags & PIN_MAPPABLE)
-			max_size = max_t(u64, max_size, vma->fence_size);
-		err = i915_vm_alloc_pt_stash(vma->vm, &work->stash, max_size);
-		if (err)
-			goto err_fence;
+	/* Allocate enough page directories to cover worst case */
+	max_size = max(size, vma->size);
+	if (flags & PIN_MAPPABLE)
+		max_size = max_t(u64, max_size, vma->fence_size);
+	err = i915_vm_alloc_pt_stash(vma->vm, &work->stash, max_size);
+	if (err)
+		goto err_fence;
 
-		err = i915_vm_pin_pt_stash(vma->vm, &work->stash);
-		if (err)
-			goto err_fence;
-	}
+	err = i915_vm_pin_pt_stash(vma->vm, &work->stash);
+	if (err)
+		goto err_fence;
 
 	/*
 	 * Differentiate between user/kernel vma inside the aliasing-ppgtt.
@@ -982,16 +929,12 @@ int i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
 			__i915_vma_set_map_and_fenceable(vma);
 	}
 
-	GEM_BUG_ON(!vma->pages);
 	err = i915_vma_bind(vma,
 			    vma->obj ? vma->obj->cache_level : 0,
 			    flags, work);
 	if (err)
 		goto err_remove;
 
-	/* 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);
 	GEM_BUG_ON(!i915_vma_is_active(vma));
 
@@ -1010,12 +953,12 @@ int i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
 err_unlock:
 	mutex_unlock(&vma->vm->mutex);
 err_fence:
-	if (work)
-		dma_fence_work_commit_imm(&work->base);
+	dma_fence_work_commit_imm(&work->base);
 err_rpm:
 	if (wakeref)
 		intel_runtime_pm_put(&vma->vm->i915->runtime_pm, wakeref);
-	i915_vma_put_pages(vma);
+	if (vma->obj)
+		i915_gem_object_unpin_pages(vma->obj);
 	return err;
 }
 
@@ -1271,6 +1214,8 @@ int i915_vma_move_to_active(struct i915_vma *vma,
 
 void __i915_vma_evict(struct i915_vma *vma)
 {
+	int count;
+
 	GEM_BUG_ON(i915_vma_is_pinned(vma));
 
 	if (i915_vma_is_map_and_fenceable(vma)) {
@@ -1305,11 +1250,19 @@ void __i915_vma_evict(struct i915_vma *vma)
 		trace_i915_vma_unbind(vma);
 		vma->ops->unbind_vma(vma->vm, vma);
 	}
+	count = hweight32(atomic_read(&vma->flags) & I915_VMA_BIND_MASK);
 	atomic_and(~(I915_VMA_BIND_MASK | I915_VMA_ERROR | I915_VMA_GGTT_WRITE),
 		   &vma->flags);
 
 	i915_vma_detach(vma);
-	vma_unbind_pages(vma);
+
+	if (vma->pages)
+		vma->ops->clear_pages(vma);
+
+	if (vma->obj) {
+		while (count--)
+			__i915_gem_object_unpin_pages(vma->obj);
+	}
 }
 
 int __i915_vma_unbind(struct i915_vma *vma)
diff --git a/drivers/gpu/drm/i915/i915_vma_types.h b/drivers/gpu/drm/i915/i915_vma_types.h
index 9e9082dc8f4b..02c1640bb034 100644
--- a/drivers/gpu/drm/i915/i915_vma_types.h
+++ b/drivers/gpu/drm/i915/i915_vma_types.h
@@ -251,11 +251,6 @@ struct i915_vma {
 
 	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.
@@ -279,4 +274,3 @@ struct i915_vma {
 };
 
 #endif
-
diff --git a/drivers/gpu/drm/i915/mm/i915_acquire_ctx.c b/drivers/gpu/drm/i915/mm/i915_acquire_ctx.c
index d1c3b958c15d..57c5bd3ab102 100644
--- a/drivers/gpu/drm/i915/mm/i915_acquire_ctx.c
+++ b/drivers/gpu/drm/i915/mm/i915_acquire_ctx.c
@@ -89,8 +89,17 @@ int i915_acquire_ctx_lock(struct i915_acquire_ctx *ctx,
 	return err;
 }
 
-int i915_acquire_mm(struct i915_acquire_ctx *acquire)
+int i915_acquire_mm(struct i915_acquire_ctx *ctx)
 {
+	struct i915_acquire *lnk;
+	int err;
+
+	for (lnk = ctx->locked; lnk; lnk = lnk->next) {
+		err = __i915_gem_object_get_pages_locked(lnk->obj);
+		if (err)
+			return err;
+	}
+
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c b/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
index af8205a2bd8f..e5e6973eb6ea 100644
--- a/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
+++ b/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
@@ -1245,9 +1245,9 @@ static void track_vma_bind(struct i915_vma *vma)
 	__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;
+	__i915_gem_object_pin_pages(obj);
+	atomic_or(I915_VMA_GLOBAL_BIND, &vma->flags);
 
 	mutex_lock(&vma->vm->mutex);
 	list_add_tail(&vma->vm_link, &vma->vm->bound_list);
diff --git a/drivers/gpu/drm/i915/selftests/intel_memory_region.c b/drivers/gpu/drm/i915/selftests/intel_memory_region.c
index 6e80d99048e4..8d9fdf591514 100644
--- a/drivers/gpu/drm/i915/selftests/intel_memory_region.c
+++ b/drivers/gpu/drm/i915/selftests/intel_memory_region.c
@@ -24,6 +24,15 @@
 #include "selftests/igt_flush_test.h"
 #include "selftests/i915_random.h"
 
+static void close_object(struct drm_i915_gem_object *obj)
+{
+	i915_gem_object_lock(obj);
+	__i915_gem_object_put_pages(obj);
+	i915_gem_object_unlock(obj);
+
+	i915_gem_object_put(obj);
+}
+
 static void close_objects(struct intel_memory_region *mem,
 			  struct list_head *objects)
 {
@@ -33,10 +42,9 @@ static void close_objects(struct intel_memory_region *mem,
 	list_for_each_entry_safe(obj, on, objects, st_link) {
 		if (i915_gem_object_has_pinned_pages(obj))
 			i915_gem_object_unpin_pages(obj);
-		/* No polluting the memory region between tests */
-		__i915_gem_object_put_pages(obj);
 		list_del(&obj->st_link);
-		i915_gem_object_put(obj);
+		/* No polluting the memory region between tests */
+		close_object(obj);
 	}
 
 	cond_resched();
@@ -124,9 +132,8 @@ igt_object_create(struct intel_memory_region *mem,
 static void igt_object_release(struct drm_i915_gem_object *obj)
 {
 	i915_gem_object_unpin_pages(obj);
-	__i915_gem_object_put_pages(obj);
 	list_del(&obj->st_link);
-	i915_gem_object_put(obj);
+	close_object(obj);
 }
 
 static int igt_mock_contiguous(void *arg)
-- 
2.20.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [Intel-gfx] [PATCH 20/20] drm/i915: Track i915_vma with its own reference counter
  2020-07-06  6:19 [Intel-gfx] s/obj->mm.lock// Chris Wilson
                   ` (18 preceding siblings ...)
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 19/20] drm/i915/gem: Replace i915_gem_object.mm.mutex with reservation_ww_class Chris Wilson
@ 2020-07-06  6:19 ` Chris Wilson
  2020-07-06  6:28 ` [Intel-gfx] ✗ Fi.CI.CHECKPATCH: warning for series starting with [01/20] drm/i915: Preallocate stashes for vma page-directories Patchwork
                   ` (4 subsequent siblings)
  24 siblings, 0 replies; 65+ messages in thread
From: Chris Wilson @ 2020-07-06  6:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: Chris Wilson

Over time, we have increasingly viewed objects from the perspective of
the GPU (tracking the address space, iova and backing pages) rather than
the perspective of GEM userspace. A single GEM handle may compromise
multiple i915_vma, each of which are tracked separately for HW
utilisation (and as a whole for GEM implicit fences). As the low level
and predominant object, it will be much easier to turn i915_vma into a
first class object with its own reference counting. Today, every
i915_vma (with a couple of notable exceptions, and some very complicated
release handling) is owned by a GEM object. It carries /no/ reference
for its own lifetime, for it exists for as long as the GEM object does.

Since today the GEM object serves as a lookup cache for all of its vma,
we need to incorporate a similar persistence into the i915_vma lifetime.
For example, the GGTT vma used for mmaps. If we only instantiate a vma
for the faulthandler, as soon as we complete the faulthandler the vma is
released, along with it the new GGTT binding causing a fresh fault. So
on top of the vma reference counting, we also have the vma open
counting, and every time we have an active userspace access we hold it
open. On closing, we drop the reference and move th vma to a short-lived
cache. This gives us the appearance of persistence like we used to have
for GGTT and even ppGTT vma, albeit the trade-off in that the vma does
not remain bound for as long the object exists (providing there is no
eviction pressure).

To reduce pressure on the GGTT cache, some of the borrowed GGTT
references (for relocation handling, pread, pwrite) were dropped in
favour of using the temporary GGTT PTE.

Aside from emulating the previous persistence of the GEM lookup cache,
the next complication stems from the i915_vma now being unreferenced
from random contexts, including from softirq. As such, and without
taking an explicit reference for a binding, we have to contend with
performing an unbind on release, and decoupling from the object. As the
former requires a mutex, we punt that to a worker and simultaneously
drop the object reference there (simply to avoid contanimating the
spinlock with the need to save/restore irq).

The random untimely unreferences may also strike places that assume that
GGTT vma are persistent. Hopefully most have been caught.

The churn in the patch stems from adding a reference to all vma lookups
that must be released when no longer required. There are a lot.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 drivers/gpu/drm/i915/display/intel_display.c  |  43 +--
 drivers/gpu/drm/i915/display/intel_display.h  |   2 +-
 .../drm/i915/display/intel_display_types.h    |   2 +-
 drivers/gpu/drm/i915/display/intel_dsb.c      |  40 +-
 drivers/gpu/drm/i915/display/intel_fbdev.c    |   6 +-
 drivers/gpu/drm/i915/display/intel_overlay.c  |  37 +-
 .../gpu/drm/i915/gem/i915_gem_client_blt.c    |   6 +-
 drivers/gpu/drm/i915/gem/i915_gem_context.c   |  40 +-
 drivers/gpu/drm/i915/gem/i915_gem_domain.c    |  13 +-
 .../gpu/drm/i915/gem/i915_gem_execbuffer.c    |  25 +-
 drivers/gpu/drm/i915/gem/i915_gem_mman.c      |  54 ++-
 drivers/gpu/drm/i915/gem/i915_gem_object.c    |  25 --
 drivers/gpu/drm/i915/gem/i915_gem_object.h    |   1 -
 .../gpu/drm/i915/gem/i915_gem_object_blt.c    |  21 +-
 drivers/gpu/drm/i915/gem/i915_gem_userptr.c   |   3 +-
 drivers/gpu/drm/i915/gem/i915_gem_wait.c      |   9 +-
 .../gpu/drm/i915/gem/selftests/huge_pages.c   | 123 ++++---
 .../i915/gem/selftests/i915_gem_client_blt.c  |  10 +-
 .../i915/gem/selftests/i915_gem_coherency.c   |  10 +-
 .../drm/i915/gem/selftests/i915_gem_context.c |  46 ++-
 .../i915/gem/selftests/i915_gem_execbuffer.c  |   4 +-
 .../drm/i915/gem/selftests/i915_gem_mman.c    |  68 ++--
 .../drm/i915/gem/selftests/igt_gem_utils.c    |  17 +-
 drivers/gpu/drm/i915/gt/gen6_ppgtt.c          |   2 +-
 drivers/gpu/drm/i915/gt/intel_engine_cs.c     |  11 +-
 drivers/gpu/drm/i915/gt/intel_ggtt.c          |   1 +
 drivers/gpu/drm/i915/gt/intel_gt.c            |  30 +-
 drivers/gpu/drm/i915/gt/intel_gt_pm.c         |   1 -
 drivers/gpu/drm/i915/gt/intel_gt_types.h      |   5 +-
 drivers/gpu/drm/i915/gt/intel_gtt.c           |  91 +++--
 drivers/gpu/drm/i915/gt/intel_gtt.h           |  20 +-
 drivers/gpu/drm/i915/gt/intel_lrc.c           |  36 +-
 drivers/gpu/drm/i915/gt/intel_renderstate.c   |  13 +-
 drivers/gpu/drm/i915/gt/intel_ring.c          |   7 +-
 .../gpu/drm/i915/gt/intel_ring_submission.c   |  27 +-
 drivers/gpu/drm/i915/gt/intel_timeline.c      |   3 +-
 drivers/gpu/drm/i915/gt/intel_workarounds.c   |  14 +-
 drivers/gpu/drm/i915/gt/selftest_engine_cs.c  |  48 +--
 drivers/gpu/drm/i915/gt/selftest_hangcheck.c  |  43 ++-
 drivers/gpu/drm/i915/gt/selftest_lrc.c        |  97 ++---
 drivers/gpu/drm/i915/gt/selftest_mocs.c       |   7 +-
 .../drm/i915/gt/selftest_ring_submission.c    |  11 +-
 drivers/gpu/drm/i915/gt/selftest_rps.c        |   7 +-
 .../gpu/drm/i915/gt/selftest_workarounds.c    |  18 +-
 drivers/gpu/drm/i915/gt/uc/intel_guc.c        |  11 +-
 drivers/gpu/drm/i915/gvt/scheduler.c          |   4 +-
 drivers/gpu/drm/i915/i915_debugfs.c           |   2 +
 drivers/gpu/drm/i915/i915_drv.c               |  15 +-
 drivers/gpu/drm/i915/i915_drv.h               |   9 +-
 drivers/gpu/drm/i915/i915_gem.c               | 154 +++-----
 drivers/gpu/drm/i915/i915_perf.c              |  37 +-
 drivers/gpu/drm/i915/i915_vma.c               | 205 ++++++-----
 drivers/gpu/drm/i915/i915_vma.h               |  53 +--
 drivers/gpu/drm/i915/i915_vma_types.h         |   2 +
 drivers/gpu/drm/i915/intel_memory_region.c    |  10 +
 .../gpu/drm/i915/selftests/i915_gem_evict.c   |  60 ++-
 drivers/gpu/drm/i915/selftests/i915_gem_gtt.c | 342 +++++++-----------
 drivers/gpu/drm/i915/selftests/i915_request.c |  16 +-
 drivers/gpu/drm/i915/selftests/i915_vma.c     |  94 +++--
 drivers/gpu/drm/i915/selftests/igt_spinner.c  |  12 +-
 .../drm/i915/selftests/intel_memory_region.c  |   5 +-
 .../gpu/drm/i915/selftests/mock_gem_device.c  |  31 +-
 drivers/gpu/drm/i915/selftests/mock_gtt.c     |   1 +
 63 files changed, 1094 insertions(+), 1066 deletions(-)

diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c
index dff7c17f3d2b..eaa8dc79b943 100644
--- a/drivers/gpu/drm/i915/display/intel_display.c
+++ b/drivers/gpu/drm/i915/display/intel_display.c
@@ -2292,7 +2292,7 @@ intel_pin_and_fence_fb_obj(struct drm_framebuffer *fb,
 		 */
 		ret = i915_vma_pin_fence(vma);
 		if (ret != 0 && INTEL_GEN(dev_priv) < 4) {
-			i915_gem_object_unpin_from_display_plane(vma);
+			i915_ggtt_unpin(vma);
 			vma = ERR_PTR(ret);
 			goto err;
 		}
@@ -2301,22 +2301,20 @@ intel_pin_and_fence_fb_obj(struct drm_framebuffer *fb,
 			*out_flags |= PLANE_HAS_FENCE;
 	}
 
-	i915_vma_get(vma);
+	i915_vma_open(vma);
 err:
 	atomic_dec(&dev_priv->gpu_error.pending_fb_pin);
 	intel_runtime_pm_put(&dev_priv->runtime_pm, wakeref);
 	return vma;
 }
 
-void intel_unpin_fb_vma(struct i915_vma *vma, unsigned long flags)
+void intel_unpin_fb_vma(struct i915_vma **vma, unsigned long flags)
 {
-	i915_gem_object_lock(vma->obj);
-	if (flags & PLANE_HAS_FENCE)
-		i915_vma_unpin_fence(vma);
-	i915_gem_object_unpin_from_display_plane(vma);
-	i915_gem_object_unlock(vma->obj);
+	if (!*vma)
+		return;
 
-	i915_vma_put(vma);
+	i915_vma_close(*vma);
+	__i915_ggtt_unpin(vma, flags);
 }
 
 static int intel_fb_pitch(const struct drm_framebuffer *fb, int color_plane,
@@ -3443,24 +3441,26 @@ initial_plane_vma(struct drm_i915_private *i915,
 		break;
 	default:
 		MISSING_CASE(plane_config->tiling);
-		goto err_obj;
+		i915_gem_object_put(obj);
+		return NULL;
 	}
 
 	vma = i915_vma_instance(obj, &i915->ggtt.vm, NULL);
+	i915_gem_object_put(obj);
 	if (IS_ERR(vma))
-		goto err_obj;
+		return vma;
 
 	if (i915_ggtt_pin(vma, 0, PIN_MAPPABLE | PIN_OFFSET_FIXED | base))
-		goto err_obj;
+		goto err_put_vma;
 
 	if (i915_gem_object_is_tiled(obj) &&
 	    !i915_vma_is_map_and_fenceable(vma))
-		goto err_obj;
+		goto err_put_vma;
 
 	return vma;
 
-err_obj:
-	i915_gem_object_put(obj);
+err_put_vma:
+	i915_vma_put(vma);
 	return NULL;
 }
 
@@ -3660,6 +3660,7 @@ intel_find_initial_plane_obj(struct intel_crtc *intel_crtc,
 	intel_state->color_plane[0].stride =
 		intel_fb_pitch(fb, 0, intel_state->hw.rotation);
 
+	i915_vma_open(vma);
 	__i915_vma_pin(vma);
 	intel_state->vma = i915_vma_get(vma);
 	if (intel_plane_uses_fence(intel_state) && i915_vma_pin_fence(vma) == 0)
@@ -15899,11 +15900,7 @@ static int intel_plane_pin_fb(struct intel_plane_state *plane_state)
 
 static void intel_plane_unpin_fb(struct intel_plane_state *old_plane_state)
 {
-	struct i915_vma *vma;
-
-	vma = fetch_and_zero(&old_plane_state->vma);
-	if (vma)
-		intel_unpin_fb_vma(vma, old_plane_state->flags);
+	intel_unpin_fb_vma(&old_plane_state->vma, old_plane_state->flags);
 }
 
 static void fb_obj_bump_render_priority(struct drm_i915_gem_object *obj)
@@ -15981,13 +15978,7 @@ intel_prepare_plane_fb(struct drm_plane *_plane,
 	if (!obj)
 		return 0;
 
-	ret = i915_gem_object_pin_pages(obj);
-	if (ret)
-		return ret;
-
 	ret = intel_plane_pin_fb(new_plane_state);
-
-	i915_gem_object_unpin_pages(obj);
 	if (ret)
 		return ret;
 
diff --git a/drivers/gpu/drm/i915/display/intel_display.h b/drivers/gpu/drm/i915/display/intel_display.h
index e890c8fb779b..77661d286dbf 100644
--- a/drivers/gpu/drm/i915/display/intel_display.h
+++ b/drivers/gpu/drm/i915/display/intel_display.h
@@ -555,7 +555,7 @@ intel_pin_and_fence_fb_obj(struct drm_framebuffer *fb,
 			   const struct i915_ggtt_view *view,
 			   bool uses_fence,
 			   unsigned long *out_flags);
-void intel_unpin_fb_vma(struct i915_vma *vma, unsigned long flags);
+void intel_unpin_fb_vma(struct i915_vma **vma, unsigned long flags);
 struct drm_framebuffer *
 intel_framebuffer_create(struct drm_i915_gem_object *obj,
 			 struct drm_mode_fb_cmd2 *mode_cmd);
diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h
index e8f809161c75..00ab3fbb27d0 100644
--- a/drivers/gpu/drm/i915/display/intel_display_types.h
+++ b/drivers/gpu/drm/i915/display/intel_display_types.h
@@ -523,7 +523,7 @@ struct intel_plane_state {
 	struct i915_ggtt_view view;
 	struct i915_vma *vma;
 	unsigned long flags;
-#define PLANE_HAS_FENCE BIT(0)
+#define PLANE_HAS_FENCE I915_VMA_RELEASE_FENCE
 
 	struct {
 		u32 offset;
diff --git a/drivers/gpu/drm/i915/display/intel_dsb.c b/drivers/gpu/drm/i915/display/intel_dsb.c
index 566fa72427b3..58d62c55ed8f 100644
--- a/drivers/gpu/drm/i915/display/intel_dsb.c
+++ b/drivers/gpu/drm/i915/display/intel_dsb.c
@@ -261,11 +261,10 @@ void intel_dsb_prepare(struct intel_crtc_state *crtc_state)
 {
 	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
 	struct drm_i915_private *i915 = to_i915(crtc->base.dev);
-	struct intel_dsb *dsb;
 	struct drm_i915_gem_object *obj;
+	struct intel_dsb *dsb;
 	struct i915_vma *vma;
 	u32 *buf;
-	intel_wakeref_t wakeref;
 
 	if (!HAS_DSB(i915))
 		return;
@@ -276,39 +275,32 @@ void intel_dsb_prepare(struct intel_crtc_state *crtc_state)
 		return;
 	}
 
-	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
-
 	obj = i915_gem_object_create_internal(i915, DSB_BUF_SIZE);
-	if (IS_ERR(obj)) {
-		drm_err(&i915->drm, "Gem object creation failed\n");
-		kfree(dsb);
-		goto out;
-	}
+	if (IS_ERR(obj))
+		goto err_dsb;
 
 	vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0, 0);
-	if (IS_ERR(vma)) {
-		drm_err(&i915->drm, "Vma creation failed\n");
-		i915_gem_object_put(obj);
-		kfree(dsb);
-		goto out;
-	}
+	i915_gem_object_put(obj);
+	if (IS_ERR(vma))
+		goto err_dsb;
 
 	buf = i915_gem_object_pin_map(vma->obj, I915_MAP_WC);
-	if (IS_ERR(buf)) {
-		drm_err(&i915->drm, "Command buffer creation failed\n");
-		i915_vma_unpin_and_release(&vma, I915_VMA_RELEASE_MAP);
-		kfree(dsb);
-		goto out;
-	}
+	if (IS_ERR(buf))
+		goto err_unpin;
 
 	dsb->id = DSB1;
 	dsb->vma = vma;
 	dsb->cmd_buf = buf;
 	dsb->free_pos = 0;
 	dsb->ins_start_offset = 0;
+
 	crtc_state->dsb = dsb;
-out:
-	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
+	return;
+
+err_unpin:
+	i915_ggtt_unpin(vma);
+err_dsb:
+	kfree(dsb);
 }
 
 /**
@@ -323,7 +315,7 @@ void intel_dsb_cleanup(struct intel_crtc_state *crtc_state)
 	if (!crtc_state->dsb)
 		return;
 
-	i915_vma_unpin_and_release(&crtc_state->dsb->vma, I915_VMA_RELEASE_MAP);
+	__i915_ggtt_unpin(&crtc_state->dsb->vma, I915_VMA_RELEASE_MAP);
 	kfree(crtc_state->dsb);
 	crtc_state->dsb = NULL;
 }
diff --git a/drivers/gpu/drm/i915/display/intel_fbdev.c b/drivers/gpu/drm/i915/display/intel_fbdev.c
index bd39eb6a21b8..2d22d9fb1731 100644
--- a/drivers/gpu/drm/i915/display/intel_fbdev.c
+++ b/drivers/gpu/drm/i915/display/intel_fbdev.c
@@ -272,7 +272,7 @@ static int intelfb_create(struct drm_fb_helper *helper,
 	return 0;
 
 out_unpin:
-	intel_unpin_fb_vma(vma, flags);
+	intel_unpin_fb_vma(&vma, flags);
 out_unlock:
 	intel_runtime_pm_put(&dev_priv->runtime_pm, wakeref);
 	return ret;
@@ -291,9 +291,7 @@ static void intel_fbdev_destroy(struct intel_fbdev *ifbdev)
 
 	drm_fb_helper_fini(&ifbdev->helper);
 
-	if (ifbdev->vma)
-		intel_unpin_fb_vma(ifbdev->vma, ifbdev->vma_flags);
-
+	intel_unpin_fb_vma(&ifbdev->vma, ifbdev->vma_flags);
 	if (ifbdev->fb)
 		drm_framebuffer_remove(&ifbdev->fb->base);
 
diff --git a/drivers/gpu/drm/i915/display/intel_overlay.c b/drivers/gpu/drm/i915/display/intel_overlay.c
index 52b4f6193b4c..c99c0975194b 100644
--- a/drivers/gpu/drm/i915/display/intel_overlay.c
+++ b/drivers/gpu/drm/i915/display/intel_overlay.c
@@ -190,7 +190,7 @@ struct intel_overlay {
 	u32 brightness, contrast, saturation;
 	u32 old_xscale, old_yscale;
 	/* register access */
-	struct drm_i915_gem_object *reg_bo;
+	struct i915_vma *reg_vma;
 	struct overlay_registers __iomem *regs;
 	u32 flip_addr;
 	/* flip handling */
@@ -302,10 +302,7 @@ static void intel_overlay_flip_prepare(struct intel_overlay *overlay,
 				       INTEL_FRONTBUFFER_OVERLAY(pipe));
 
 	overlay->old_vma = overlay->vma;
-	if (vma)
-		overlay->vma = i915_vma_get(vma);
-	else
-		overlay->vma = NULL;
+	overlay->vma = vma;
 }
 
 /* overlay needs to be enabled in OCMD reg */
@@ -350,17 +347,9 @@ static int intel_overlay_continue(struct intel_overlay *overlay,
 
 static void intel_overlay_release_old_vma(struct intel_overlay *overlay)
 {
-	struct i915_vma *vma;
-
-	vma = fetch_and_zero(&overlay->old_vma);
-	if (drm_WARN_ON(&overlay->i915->drm, !vma))
-		return;
-
 	intel_frontbuffer_flip_complete(overlay->i915,
 					INTEL_FRONTBUFFER_OVERLAY(overlay->crtc->pipe));
-
-	i915_gem_object_unpin_from_display_plane(vma);
-	i915_vma_put(vma);
+	__i915_ggtt_unpin(&overlay->old_vma, 0);
 }
 
 static void
@@ -860,10 +849,9 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay,
 	return 0;
 
 out_unpin:
-	i915_gem_object_unpin_from_display_plane(vma);
+	i915_ggtt_unpin(vma);
 out_pin_section:
 	atomic_dec(&dev_priv->gpu_error.pending_fb_pin);
-
 	return ret;
 }
 
@@ -1322,10 +1310,9 @@ static int get_registers(struct intel_overlay *overlay, bool use_phys)
 		return PTR_ERR(obj);
 
 	vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0, PIN_MAPPABLE);
-	if (IS_ERR(vma)) {
-		err = PTR_ERR(vma);
-		goto err_put_bo;
-	}
+	i915_gem_object_put(obj);
+	if (IS_ERR(vma))
+		return PTR_ERR(vma);
 
 	if (use_phys)
 		overlay->flip_addr = sg_dma_address(obj->mm.pages->sgl);
@@ -1336,14 +1323,14 @@ static int get_registers(struct intel_overlay *overlay, bool use_phys)
 
 	if (IS_ERR(overlay->regs)) {
 		err = PTR_ERR(overlay->regs);
-		goto err_put_bo;
+		goto err_vma;
 	}
 
-	overlay->reg_bo = obj;
+	overlay->reg_vma = vma;
 	return 0;
 
-err_put_bo:
-	i915_gem_object_put(obj);
+err_vma:
+	i915_vma_put(vma);
 	return err;
 }
 
@@ -1408,7 +1395,7 @@ void intel_overlay_cleanup(struct drm_i915_private *dev_priv)
 	 */
 	drm_WARN_ON(&dev_priv->drm, overlay->active);
 
-	i915_gem_object_put(overlay->reg_bo);
+	i915_vma_put(overlay->reg_vma);
 	i915_active_fini(&overlay->last_flip);
 
 	kfree(overlay);
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_client_blt.c b/drivers/gpu/drm/i915/gem/i915_gem_client_blt.c
index 947c8aa8e13e..a097a0d8e347 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_client_blt.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_client_blt.c
@@ -88,6 +88,7 @@ static struct i915_sleeve *create_sleeve(struct i915_address_space *vm,
 
 static void destroy_sleeve(struct i915_sleeve *sleeve)
 {
+	i915_vma_put(sleeve->vma);
 	kfree(sleeve);
 }
 
@@ -116,7 +117,8 @@ static void clear_pages_work_release(struct dma_fence *fence)
 {
 	struct clear_pages_work *w = container_of(fence, typeof(*w), dma);
 
-	destroy_sleeve(w->sleeve);
+	if (w->sleeve)
+		destroy_sleeve(w->sleeve);
 
 	i915_sw_fence_fini(&w->wait);
 
@@ -233,6 +235,8 @@ static void clear_pages_worker(struct work_struct *work)
 		dma_fence_signal(&w->dma);
 		dma_fence_put(&w->dma);
 	}
+
+	destroy_sleeve(fetch_and_zero(&w->sleeve));
 }
 
 static int __i915_sw_fence_call
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context.c b/drivers/gpu/drm/i915/gem/i915_gem_context.c
index bd68746327b3..ad28a6122165 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_context.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_context.c
@@ -105,33 +105,37 @@ static void lut_close(struct i915_gem_context *ctx)
 	rcu_read_lock();
 	radix_tree_for_each_slot(slot, &ctx->handles_vma, &iter, 0) {
 		struct i915_vma *vma = rcu_dereference_raw(*slot);
-		struct drm_i915_gem_object *obj = vma->obj;
 		struct i915_lut_handle *lut;
 
-		if (!kref_get_unless_zero(&obj->base.refcount))
+		vma = i915_vma_tryget(vma);
+		if (!vma)
 			continue;
 
-		spin_lock(&obj->lut_lock);
-		list_for_each_entry(lut, &obj->lut_list, obj_link) {
-			if (lut->ctx != ctx)
-				continue;
+		if (vma->obj) {
+			struct drm_i915_gem_object *obj = vma->obj;
 
-			if (lut->handle != iter.index)
-				continue;
+			spin_lock(&obj->lut_lock);
+			list_for_each_entry(lut, &obj->lut_list, obj_link) {
+				if (lut->ctx != ctx)
+					continue;
 
-			list_del(&lut->obj_link);
-			break;
-		}
-		spin_unlock(&obj->lut_lock);
+				if (lut->handle != iter.index)
+					continue;
+
+				list_del(&lut->obj_link);
+				break;
+			}
+			spin_unlock(&obj->lut_lock);
 
-		if (&lut->obj_link != &obj->lut_list) {
-			i915_lut_handle_free(lut);
-			radix_tree_iter_delete(&ctx->handles_vma, &iter, slot);
-			i915_vma_close(vma);
-			i915_gem_object_put(obj);
+			if (&lut->obj_link != &obj->lut_list) {
+				i915_lut_handle_free(lut);
+				radix_tree_iter_delete(&ctx->handles_vma,
+						       &iter, slot);
+				i915_vma_put(vma);
+			}
 		}
 
-		i915_gem_object_put(obj);
+		i915_vma_put(vma);
 	}
 	rcu_read_unlock();
 	mutex_unlock(&ctx->lut_mutex);
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_domain.c b/drivers/gpu/drm/i915/gem/i915_gem_domain.c
index 30e4b163588b..ac92bc2f597a 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_domain.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_domain.c
@@ -208,9 +208,7 @@ int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj,
 	i915_gem_object_unlock(obj);
 
 	/* The cache-level will be applied when each vma is rebound. */
-	return i915_gem_object_unbind(obj,
-				      I915_GEM_OBJECT_UNBIND_ACTIVE |
-				      I915_GEM_OBJECT_UNBIND_BARRIER);
+	return i915_gem_object_unbind(obj, I915_GEM_OBJECT_UNBIND_ACTIVE);
 }
 
 int i915_gem_get_caching_ioctl(struct drm_device *dev, void *data,
@@ -394,15 +392,6 @@ static void i915_gem_object_bump_inactive_ggtt(struct drm_i915_gem_object *obj)
 	}
 }
 
-void
-i915_gem_object_unpin_from_display_plane(struct i915_vma *vma)
-{
-	/* Bump the LRU to try and avoid premature eviction whilst flipping  */
-	i915_gem_object_bump_inactive_ggtt(vma->obj);
-
-	i915_vma_unpin(vma);
-}
-
 /**
  * Moves a single object to the CPU read, and possibly write domain.
  * @obj: object to act on
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
index 37c0d5058891..e7f127b75b13 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
@@ -1629,7 +1629,7 @@ static int eb_alloc_cmdparser(struct i915_execbuffer *eb)
 	i915_gem_object_set_cache_coherency(vma->obj, I915_CACHE_LLC);
 	vma->private = pool;
 
-	ev->vma = i915_vma_get(vma);
+	ev->vma = vma;
 	ev->exec = &no_entry;
 	list_add(&ev->reloc_link, &eb->array->aux_list);
 	list_add(&ev->bind_link, &eb->bind_list);
@@ -1659,7 +1659,7 @@ static int eb_alloc_cmdparser(struct i915_execbuffer *eb)
 			}
 			vma->private = pool;
 
-			ev->vma = i915_vma_get(vma);
+			ev->vma = vma;
 			ev->exec = &no_entry;
 			list_add(&ev->reloc_link, &eb->array->aux_list);
 			list_add(&ev->bind_link, &eb->bind_list);
@@ -1707,7 +1707,7 @@ static int eb_secure_batch(struct i915_execbuffer *eb)
 			return PTR_ERR(vma);
 		}
 
-		ev->vma = i915_vma_get(vma);
+		ev->vma = vma;
 		ev->exec = &no_entry;
 
 		list_add(&ev->submit_link, &eb->submit_list);
@@ -1760,9 +1760,7 @@ static int __eb_add_lut(struct i915_execbuffer *eb,
 	if (unlikely(!lut))
 		return -ENOMEM;
 
-	i915_vma_get(vma);
-	if (!atomic_fetch_inc(&vma->open_count))
-		i915_vma_reopen(vma);
+	i915_vma_open(vma);
 	lut->handle = handle;
 	lut->ctx = ctx;
 
@@ -1798,7 +1796,6 @@ static int __eb_add_lut(struct i915_execbuffer *eb,
 
 err:
 	i915_vma_close(vma);
-	i915_vma_put(vma);
 	i915_lut_handle_free(lut);
 	return err;
 }
@@ -1825,16 +1822,15 @@ static struct i915_vma *eb_lookup_vma(struct i915_execbuffer *eb, u32 handle)
 			return ERR_PTR(-ENOENT);
 
 		vma = i915_vma_instance(obj, vm, NULL);
-		if (IS_ERR(vma)) {
-			i915_gem_object_put(obj);
+		i915_gem_object_put(obj);
+		if (IS_ERR(vma))
 			return vma;
-		}
 
 		err = __eb_add_lut(eb, handle, vma);
 		if (likely(!err))
 			return vma;
 
-		i915_gem_object_put(obj);
+		i915_vma_put(vma);
 		if (err != -EEXIST)
 			return ERR_PTR(err);
 	} while (1);
@@ -2179,14 +2175,13 @@ eb_relocs_grow(struct i915_execbuffer *eb, unsigned long *count)
 							    I915_CACHE_LLC);
 
 		vma = i915_vma_instance(obj, eb->context->vm, NULL);
-		if (IS_ERR(vma)) {
-			i915_gem_object_put(obj);
+		i915_gem_object_put(obj);
+		if (IS_ERR(vma))
 			return ERR_CAST(vma);
-		}
 
 		ev = kzalloc(sizeof(*ev), GFP_KERNEL);
 		if (!ev) {
-			i915_gem_object_put(obj);
+			i915_vma_put(vma);
 			return ERR_PTR(-ENOMEM);
 		}
 
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_mman.c b/drivers/gpu/drm/i915/gem/i915_gem_mman.c
index b23368529a40..35bf8a0879a3 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_mman.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_mman.c
@@ -365,16 +365,19 @@ static vm_fault_t vm_fault_gtt(struct vm_fault *vmf)
 	assert_rpm_wakelock_held(rpm);
 
 	/* Mark as being mmapped into userspace for later revocation */
-	mutex_lock(&i915->ggtt.vm.mutex);
-	if (!i915_vma_set_userfault(vma) && !obj->userfault_count++)
-		list_add(&obj->userfault_link, &i915->ggtt.userfault_list);
-	mutex_unlock(&i915->ggtt.vm.mutex);
+	mutex_lock(&ggtt->vm.mutex);
+	if (!i915_vma_set_userfault(vma)) {
+		i915_vma_open(vma);
+		if (!obj->userfault_count++)
+			list_add(&obj->userfault_link, &ggtt->userfault_list);
+	}
+	mutex_unlock(&ggtt->vm.mutex);
 
 	/* Track the mmo associated with the fenced vma */
 	vma->mmo = mmo;
 
 	if (IS_ACTIVE(CONFIG_DRM_I915_USERFAULT_AUTOSUSPEND))
-		intel_wakeref_auto(&i915->ggtt.userfault_wakeref,
+		intel_wakeref_auto(&ggtt->userfault_wakeref,
 				   msecs_to_jiffies_timeout(CONFIG_DRM_I915_USERFAULT_AUTOSUSPEND));
 
 	if (write) {
@@ -386,7 +389,7 @@ static vm_fault_t vm_fault_gtt(struct vm_fault *vmf)
 err_fence:
 	i915_vma_unpin_fence(vma);
 err_unpin:
-	__i915_vma_unpin(vma);
+	i915_ggtt_unpin(vma);
 err_reset:
 	intel_gt_reset_unlock(ggtt->vm.gt, srcu);
 err_rpm:
@@ -434,8 +437,27 @@ void __i915_gem_object_release_mmap_gtt(struct drm_i915_gem_object *obj)
 
 	GEM_BUG_ON(!obj->userfault_count);
 
-	for_each_ggtt_vma(vma, obj)
+	spin_lock(&obj->vma.lock);
+	list_for_each_entry(vma, &obj->vma.list, obj_link) {
+		if (!i915_vma_is_ggtt(vma))
+			break;
+
+		if (!i915_vma_has_userfault(vma))
+			continue;
+
+		if (!i915_vma_tryget(vma))
+			continue;
+
+		spin_unlock(&obj->vma.lock);
+
 		i915_vma_revoke_mmap(vma);
+		i915_vma_put(vma);
+
+		/* Restart after dropping the lock */
+		spin_lock(&obj->vma.lock);
+		vma = list_first_entry(&obj->vma.list, typeof(*vma), obj_link);
+	}
+	spin_unlock(&obj->vma.lock);
 
 	GEM_BUG_ON(obj->userfault_count);
 }
@@ -753,12 +775,15 @@ static void vm_open(struct vm_area_struct *vma)
 	i915_gem_object_get(obj);
 }
 
-static void vm_close(struct vm_area_struct *vma)
+static void vm_close_gtt(struct vm_area_struct *vma)
 {
 	struct i915_mmap_offset *mmo = vma->vm_private_data;
 	struct drm_i915_gem_object *obj = mmo->obj;
 
-	GEM_BUG_ON(!obj);
+	/* XXX Punishes shared GGTT mmaps; per-mmo vma fault tracking? */
+	if (obj->userfault_count)
+		i915_gem_object_release_mmap_gtt(obj);
+
 	i915_gem_object_put(obj);
 }
 
@@ -766,9 +791,18 @@ static const struct vm_operations_struct vm_ops_gtt = {
 	.fault = vm_fault_gtt,
 	.access = vm_access,
 	.open = vm_open,
-	.close = vm_close,
+	.close = vm_close_gtt,
 };
 
+static void vm_close(struct vm_area_struct *vma)
+{
+	struct i915_mmap_offset *mmo = vma->vm_private_data;
+	struct drm_i915_gem_object *obj = mmo->obj;
+
+	GEM_BUG_ON(!obj);
+	i915_gem_object_put(obj);
+}
+
 static const struct vm_operations_struct vm_ops_cpu = {
 	.fault = vm_fault_cpu,
 	.access = vm_access,
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.c b/drivers/gpu/drm/i915/gem/i915_gem_object.c
index 799ad4e648aa..e8b532679635 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_object.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_object.c
@@ -141,14 +141,12 @@ void i915_gem_close_object(struct drm_gem_object *gem, struct drm_file *file)
 		vma = radix_tree_delete(&ctx->handles_vma, lut->handle);
 		if (vma) {
 			GEM_BUG_ON(vma->obj != obj);
-			GEM_BUG_ON(!atomic_read(&vma->open_count));
 			i915_vma_close(vma);
 		}
 		mutex_unlock(&ctx->lut_mutex);
 
 		i915_gem_context_put(lut->ctx);
 		i915_lut_handle_free(lut);
-		i915_gem_object_put(obj);
 	}
 }
 
@@ -196,29 +194,6 @@ static void __i915_gem_free_objects(struct drm_i915_private *i915,
 	llist_for_each_entry_safe(obj, on, freed, freed) {
 		trace_i915_gem_object_destroy(obj);
 
-		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_put(vma);
-
-				spin_lock(&obj->vma.lock);
-			}
-			spin_unlock(&obj->vma.lock);
-		}
-
 		__i915_gem_object_free_mmaps(obj);
 
 		GEM_BUG_ON(!list_empty(&obj->lut_list));
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.h b/drivers/gpu/drm/i915/gem/i915_gem_object.h
index 8f1d20f6d42a..32ab8fd4e086 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_object.h
+++ b/drivers/gpu/drm/i915/gem/i915_gem_object.h
@@ -430,7 +430,6 @@ i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj,
 				     u32 alignment,
 				     const struct i915_ggtt_view *view,
 				     unsigned int flags);
-void i915_gem_object_unpin_from_display_plane(struct i915_vma *vma);
 
 void i915_gem_object_make_unshrinkable(struct drm_i915_gem_object *obj);
 void i915_gem_object_make_shrinkable(struct drm_i915_gem_object *obj);
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object_blt.c b/drivers/gpu/drm/i915/gem/i915_gem_object_blt.c
index bfdb32d46877..5f90e93ae762 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_object_blt.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_object_blt.c
@@ -92,11 +92,13 @@ struct i915_vma *intel_emit_vma_fill_blt(struct intel_context *ce,
 
 	err = i915_vma_pin(batch, 0, 0, PIN_USER);
 	if (unlikely(err))
-		goto out_put;
+		goto out_vma;
 
 	batch->private = pool;
 	return batch;
 
+out_vma:
+	i915_vma_put(batch);
 out_put:
 	intel_gt_buffer_pool_put(pool);
 out_pm:
@@ -123,6 +125,7 @@ void intel_emit_vma_release(struct intel_context *ce, struct i915_vma *vma)
 {
 	i915_vma_unpin(vma);
 	intel_gt_buffer_pool_put(vma->private);
+	i915_vma_put(vma);
 	intel_engine_pm_put(ce->engine);
 }
 
@@ -152,7 +155,7 @@ int i915_gem_object_fill_blt(struct drm_i915_gem_object *obj,
 
 	err = i915_vma_pin(vma, 0, 0, PIN_USER);
 	if (unlikely(err))
-		return err;
+		goto out_vma;
 
 	batch = intel_emit_vma_fill_blt(ce, vma, value);
 	if (IS_ERR(batch)) {
@@ -195,6 +198,8 @@ int i915_gem_object_fill_blt(struct drm_i915_gem_object *obj,
 	intel_emit_vma_release(ce, batch);
 out_unpin:
 	i915_vma_unpin(vma);
+out_vma:
+	i915_vma_put(vma);
 	return err;
 }
 
@@ -305,11 +310,13 @@ struct i915_vma *intel_emit_vma_copy_blt(struct intel_context *ce,
 
 	err = i915_vma_pin(batch, 0, 0, PIN_USER);
 	if (unlikely(err))
-		goto out_put;
+		goto out_batch;
 
 	batch->private = pool;
 	return batch;
 
+out_batch:
+	i915_vma_put(batch);
 out_put:
 	intel_gt_buffer_pool_put(pool);
 out_pm:
@@ -334,7 +341,7 @@ int i915_gem_object_copy_blt(struct drm_i915_gem_object *src,
 
 	err = i915_vma_pin(vma[0], 0, 0, PIN_USER);
 	if (unlikely(err))
-		return err;
+		goto out_src;
 
 	vma[1] = i915_vma_instance(dst, vm, NULL);
 	if (IS_ERR(vma[1]))
@@ -342,7 +349,7 @@ int i915_gem_object_copy_blt(struct drm_i915_gem_object *src,
 
 	err = i915_vma_pin(vma[1], 0, 0, PIN_USER);
 	if (unlikely(err))
-		goto out_unpin_src;
+		goto out_dst;
 
 	batch = intel_emit_vma_copy_blt(ce, vma[0], vma[1]);
 	if (IS_ERR(batch)) {
@@ -398,8 +405,12 @@ int i915_gem_object_copy_blt(struct drm_i915_gem_object *src,
 	intel_emit_vma_release(ce, batch);
 out_unpin_dst:
 	i915_vma_unpin(vma[1]);
+out_dst:
+	i915_vma_put(vma[1]);
 out_unpin_src:
 	i915_vma_unpin(vma[0]);
+out_src:
+	i915_vma_put(vma[0]);
 	return err;
 }
 
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_userptr.c b/drivers/gpu/drm/i915/gem/i915_gem_userptr.c
index 80907c00c6fd..58f70c9e909e 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_userptr.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_userptr.c
@@ -127,8 +127,7 @@ userptr_mn_invalidate_range_start(struct mmu_notifier *_mn,
 		spin_unlock(&mn->lock);
 
 		ret = i915_gem_object_unbind(obj,
-					     I915_GEM_OBJECT_UNBIND_ACTIVE |
-					     I915_GEM_OBJECT_UNBIND_BARRIER);
+					     I915_GEM_OBJECT_UNBIND_ACTIVE);
 		if (ret == 0) {
 			/* ww_mutex and mmu_notifier is fs_reclaim tainted */
 			if (i915_gem_object_trylock(obj)) {
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_wait.c b/drivers/gpu/drm/i915/gem/i915_gem_wait.c
index 8af55cd3e690..5361d5ff95da 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_wait.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_wait.c
@@ -43,8 +43,7 @@ i915_gem_object_wait_reservation(struct dma_resv *resv,
 		unsigned int count, i;
 		int ret;
 
-		ret = dma_resv_get_fences_rcu(resv,
-							&excl, &count, &shared);
+		ret = dma_resv_get_fences_rcu(resv, &excl, &count, &shared);
 		if (ret)
 			return ret;
 
@@ -83,6 +82,12 @@ i915_gem_object_wait_reservation(struct dma_resv *resv,
 	/*
 	 * Opportunistically prune the fences iff we know they have *all* been
 	 * signaled.
+	 *
+	 * dma_resv is not autopruning, so if a fence holds a reference to the
+	 * object, we will keep the reference cycle until something else
+	 * replaces the fence. This may not happen naturally.
+	 *
+	 * Morale of this story is don't use dma_resv.
 	 */
 	if (prune_fences && dma_resv_trylock(resv)) {
 		if (dma_resv_test_signaled_rcu(resv, true))
diff --git a/drivers/gpu/drm/i915/gem/selftests/huge_pages.c b/drivers/gpu/drm/i915/gem/selftests/huge_pages.c
index a16aae4acf44..546b541ce6c5 100644
--- a/drivers/gpu/drm/i915/gem/selftests/huge_pages.c
+++ b/drivers/gpu/drm/i915/gem/selftests/huge_pages.c
@@ -412,14 +412,16 @@ static int igt_mock_exhaust_device_supported_pages(void *arg)
 			if (obj->base.size != combination) {
 				pr_err("obj->base.size=%zu, expected=%u\n",
 				       obj->base.size, combination);
+				i915_gem_object_put(obj);
 				err = -EINVAL;
-				goto out_put;
+				goto out_device;
 			}
 
 			vma = i915_vma_instance(obj, &ppgtt->vm, NULL);
+			i915_gem_object_put(obj);
 			if (IS_ERR(vma)) {
 				err = PTR_ERR(vma);
-				goto out_put;
+				goto out_device;
 			}
 
 			err = i915_vma_pin(vma, 0, 0, PIN_USER);
@@ -435,8 +437,7 @@ static int igt_mock_exhaust_device_supported_pages(void *arg)
 			}
 
 			i915_vma_unpin(vma);
-			i915_gem_object_put(obj);
-
+			i915_vma_put(vma);
 			if (err)
 				goto out_device;
 		}
@@ -445,10 +446,9 @@ static int igt_mock_exhaust_device_supported_pages(void *arg)
 	goto out_device;
 
 out_put:
-	i915_gem_object_put(obj);
+	i915_vma_put(vma);
 out_device:
 	mkwrite_device_info(i915)->page_sizes = saved_mask;
-
 	return err;
 }
 
@@ -468,7 +468,6 @@ static int igt_mock_memory_region_huge_pages(void *arg)
 	struct drm_i915_private *i915 = ppgtt->vm.i915;
 	unsigned long supported = INTEL_INFO(i915)->page_sizes;
 	struct intel_memory_region *mem;
-	struct drm_i915_gem_object *obj;
 	struct i915_vma *vma;
 	int bit;
 	int err = 0;
@@ -485,6 +484,8 @@ static int igt_mock_memory_region_huge_pages(void *arg)
 		int i;
 
 		for (i = 0; i < ARRAY_SIZE(flags); ++i) {
+			struct drm_i915_gem_object *obj;
+
 			obj = i915_gem_object_create_region(mem, page_size,
 							    flags[i]);
 			if (IS_ERR(obj)) {
@@ -493,9 +494,10 @@ static int igt_mock_memory_region_huge_pages(void *arg)
 			}
 
 			vma = i915_vma_instance(obj, &ppgtt->vm, NULL);
+			i915_gem_object_put(obj);
 			if (IS_ERR(vma)) {
 				err = PTR_ERR(vma);
-				goto out_put;
+				goto out_region;
 			}
 
 			err = i915_vma_pin(vma, 0, 0, PIN_USER);
@@ -523,7 +525,7 @@ static int igt_mock_memory_region_huge_pages(void *arg)
 			}
 
 			i915_vma_unpin(vma);
-			close_object(obj);
+			i915_vma_put(vma);
 		}
 	}
 
@@ -532,7 +534,7 @@ static int igt_mock_memory_region_huge_pages(void *arg)
 out_unpin:
 	i915_vma_unpin(vma);
 out_put:
-	i915_gem_object_put(obj);
+	i915_vma_put(vma);
 out_region:
 	intel_memory_region_put(mem);
 	return err;
@@ -543,7 +545,7 @@ static int igt_mock_ppgtt_misaligned_dma(void *arg)
 	struct i915_ppgtt *ppgtt = arg;
 	struct drm_i915_private *i915 = ppgtt->vm.i915;
 	unsigned long supported = INTEL_INFO(i915)->page_sizes;
-	struct drm_i915_gem_object *obj;
+	struct i915_vma *vma;
 	int bit;
 	int err;
 
@@ -558,12 +560,12 @@ static int igt_mock_ppgtt_misaligned_dma(void *arg)
 	for_each_set_bit_from(bit, &supported,
 			      ilog2(I915_GTT_MAX_PAGE_SIZE) + 1) {
 		IGT_TIMEOUT(end_time);
+		struct drm_i915_gem_object *obj;
 		unsigned int page_size = BIT(bit);
 		unsigned int flags = PIN_USER | PIN_OFFSET_FIXED;
 		unsigned int offset;
 		unsigned int size =
 			round_up(page_size, I915_GTT_PAGE_SIZE_2M) << 1;
-		struct i915_vma *vma;
 
 		obj = fake_huge_pages_object(i915, size, true);
 		if (IS_ERR(obj))
@@ -572,27 +574,27 @@ static int igt_mock_ppgtt_misaligned_dma(void *arg)
 		if (obj->base.size != size) {
 			pr_err("obj->base.size=%zu, expected=%u\n",
 			       obj->base.size, size);
-			err = -EINVAL;
-			goto out_put;
+			i915_gem_object_put(obj);
+			return -EINVAL;
 		}
 
 		err = i915_gem_object_pin_pages(obj);
-		if (err)
-			goto out_put;
+		if (err) {
+			i915_gem_object_put(obj);
+			return err;
+		}
 
 		/* Force the page size for this object */
 		obj->mm.page_sizes.sg = page_size;
 
 		vma = i915_vma_instance(obj, &ppgtt->vm, NULL);
-		if (IS_ERR(vma)) {
-			err = PTR_ERR(vma);
-			goto out_unpin;
-		}
+		i915_gem_object_put(obj);
+		if (IS_ERR(vma))
+			return PTR_ERR(vma);
 
 		err = i915_vma_pin(vma, 0, 0, flags);
 		if (err)
-			goto out_unpin;
-
+			goto out_vma;
 
 		err = igt_check_page_sizes(vma);
 
@@ -603,9 +605,8 @@ static int igt_mock_ppgtt_misaligned_dma(void *arg)
 		}
 
 		i915_vma_unpin(vma);
-
 		if (err)
-			goto out_unpin;
+			goto out_vma;
 
 		/*
 		 * Try all the other valid offsets until the next
@@ -615,11 +616,11 @@ static int igt_mock_ppgtt_misaligned_dma(void *arg)
 		for (offset = 4096; offset < page_size; offset += 4096) {
 			err = i915_vma_unbind(vma);
 			if (err)
-				goto out_unpin;
+				goto out_vma;
 
 			err = i915_vma_pin(vma, 0, 0, flags | offset);
 			if (err)
-				goto out_unpin;
+				goto out_vma;
 
 			err = igt_check_page_sizes(vma);
 
@@ -630,9 +631,8 @@ static int igt_mock_ppgtt_misaligned_dma(void *arg)
 			}
 
 			i915_vma_unpin(vma);
-
 			if (err)
-				goto out_unpin;
+				goto out_vma;
 
 			if (igt_timeout(end_time,
 					"%s timed out at offset %x with page-size %x\n",
@@ -640,17 +640,13 @@ static int igt_mock_ppgtt_misaligned_dma(void *arg)
 				break;
 		}
 
-		i915_gem_object_unpin_pages(obj);
-		close_object(obj);
+		i915_vma_put(vma);
 	}
 
 	return 0;
 
-out_unpin:
-	i915_gem_object_unpin_pages(obj);
-out_put:
-	i915_gem_object_put(obj);
-
+out_vma:
+	i915_vma_put(vma);
 	return err;
 }
 
@@ -713,12 +709,15 @@ static int igt_mock_ppgtt_huge_fill(void *arg)
 		}
 
 		err = i915_vma_pin(vma, 0, 0, PIN_USER);
-		if (err)
+		if (err) {
+			i915_vma_put(vma);
 			break;
+		}
 
 		err = igt_check_page_sizes(vma);
 		if (err) {
 			i915_vma_unpin(vma);
+			i915_vma_put(vma);
 			break;
 		}
 
@@ -750,6 +749,7 @@ static int igt_mock_ppgtt_huge_fill(void *arg)
 					I915_GTT_PAGE_SIZE_2M)) {
 				pr_err("node.start(%llx) not aligned to 2M\n",
 				       vma->node.start);
+				i915_vma_put(vma);
 				err = -EINVAL;
 				break;
 			}
@@ -758,6 +758,7 @@ static int igt_mock_ppgtt_huge_fill(void *arg)
 					I915_GTT_PAGE_SIZE_2M)) {
 				pr_err("node.size(%llx) not aligned to 2M\n",
 				       vma->node.size);
+				i915_vma_put(vma);
 				err = -EINVAL;
 				break;
 			}
@@ -767,10 +768,13 @@ static int igt_mock_ppgtt_huge_fill(void *arg)
 			pr_err("gtt=%u, expected=%u, size=%zd, single=%s\n",
 			       vma->page_sizes.gtt, expected_gtt,
 			       obj->base.size, yesno(!!single));
+			i915_vma_put(vma);
 			err = -EINVAL;
 			break;
 		}
 
+		i915_vma_put(vma);
+
 		if (igt_timeout(end_time,
 				"%s timed out at size %zd\n",
 				__func__, obj->base.size))
@@ -875,8 +879,10 @@ static int igt_mock_ppgtt_64K(void *arg)
 				return PTR_ERR(obj);
 
 			err = i915_gem_object_pin_pages(obj);
-			if (err)
-				goto out_object_put;
+			if (err) {
+				i915_gem_object_put(obj);
+				return err;
+			}
 
 			/*
 			 * Disable 2M pages -- We only want to use 64K/4K pages
@@ -885,17 +891,16 @@ static int igt_mock_ppgtt_64K(void *arg)
 			obj->mm.page_sizes.sg &= ~I915_GTT_PAGE_SIZE_2M;
 
 			vma = i915_vma_instance(obj, &ppgtt->vm, NULL);
-			if (IS_ERR(vma)) {
-				err = PTR_ERR(vma);
-				goto out_object_unpin;
-			}
+			i915_gem_object_put(obj);
+			if (IS_ERR(vma))
+				return PTR_ERR(vma);
 
 			if (offset)
 				flags |= PIN_OFFSET_FIXED | offset;
 
 			err = i915_vma_pin(vma, 0, 0, flags);
 			if (err)
-				goto out_object_unpin;
+				goto out_vma_put;
 
 			err = igt_check_page_sizes(vma);
 			if (err)
@@ -928,8 +933,7 @@ static int igt_mock_ppgtt_64K(void *arg)
 			}
 
 			i915_vma_unpin(vma);
-			i915_gem_object_unpin_pages(obj);
-			close_object(obj);
+			i915_vma_put(vma);
 		}
 	}
 
@@ -937,11 +941,8 @@ static int igt_mock_ppgtt_64K(void *arg)
 
 out_vma_unpin:
 	i915_vma_unpin(vma);
-out_object_unpin:
-	i915_gem_object_unpin_pages(obj);
-out_object_put:
-	i915_gem_object_put(obj);
-
+out_vma_put:
+	i915_vma_put(vma);
 	return err;
 }
 
@@ -1050,7 +1051,7 @@ static int __igt_write_huge(struct intel_context *ce,
 
 	err = i915_vma_unbind(vma);
 	if (err)
-		return err;
+		goto out_vma;
 
 	err = i915_vma_pin(vma, size, 0, flags | offset);
 	if (err) {
@@ -1061,7 +1062,7 @@ static int __igt_write_huge(struct intel_context *ce,
 		if (err == -ENOSPC && i915_is_ggtt(ce->vm))
 			err = 0;
 
-		return err;
+		goto out_vma;
 	}
 
 	err = igt_check_page_sizes(vma);
@@ -1082,6 +1083,8 @@ static int __igt_write_huge(struct intel_context *ce,
 
 out_vma_unpin:
 	i915_vma_unpin(vma);
+out_vma:
+	i915_vma_put(vma);
 	return err;
 }
 
@@ -1463,11 +1466,13 @@ static int igt_tmpfs_fallback(void *arg)
 
 	err = i915_vma_pin(vma, 0, 0, PIN_USER);
 	if (err)
-		goto out_put;
+		goto out_vma;
 
 	err = igt_check_page_sizes(vma);
 
 	i915_vma_unpin(vma);
+out_vma:
+	i915_vma_put(vma);
 out_put:
 	i915_gem_object_put(obj);
 out_restore:
@@ -1514,7 +1519,7 @@ static int igt_shrink_thp(void *arg)
 
 	err = i915_vma_pin(vma, 0, 0, flags);
 	if (err)
-		goto out_put;
+		goto out_vma;
 
 	if (obj->mm.page_sizes.phys < I915_GTT_PAGE_SIZE_2M) {
 		pr_info("failed to allocate THP, finishing test early\n");
@@ -1538,7 +1543,7 @@ static int igt_shrink_thp(void *arg)
 	i915_gem_context_unlock_engines(ctx);
 	i915_vma_unpin(vma);
 	if (err)
-		goto out_put;
+		goto out_vma;
 
 	/*
 	 * Now that the pages are *unpinned* shrink-all should invoke
@@ -1548,18 +1553,18 @@ static int igt_shrink_thp(void *arg)
 	if (i915_gem_object_has_pages(obj)) {
 		pr_err("shrink-all didn't truncate the pages\n");
 		err = -EINVAL;
-		goto out_put;
+		goto out_vma;
 	}
 
 	if (obj->mm.page_sizes.sg || obj->mm.page_sizes.phys) {
 		pr_err("residual page-size bits left\n");
 		err = -EINVAL;
-		goto out_put;
+		goto out_vma;
 	}
 
 	err = i915_vma_pin(vma, 0, 0, flags);
 	if (err)
-		goto out_put;
+		goto out_vma;
 
 	while (n--) {
 		err = cpu_check(obj, n, 0xdeadbeaf);
@@ -1569,6 +1574,8 @@ static int igt_shrink_thp(void *arg)
 
 out_unpin:
 	i915_vma_unpin(vma);
+out_vma:
+	i915_vma_put(vma);
 out_put:
 	i915_gem_object_put(obj);
 out_vm:
diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_client_blt.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_client_blt.c
index 299c29e9ad86..3f2beeda0880 100644
--- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_client_blt.c
+++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_client_blt.c
@@ -75,6 +75,13 @@ static int __igt_client_fill(struct intel_engine_cs *engine)
 		if (err)
 			goto err_unpin;
 
+		err = i915_gem_object_wait(obj,
+					   I915_WAIT_INTERRUPTIBLE |
+					   I915_WAIT_ALL,
+					   HZ / 2);
+		if (err)
+			goto err_unpin;
+
 		i915_gem_object_lock(obj);
 		err = i915_gem_object_set_to_cpu_domain(obj, false);
 		i915_gem_object_unlock(obj);
@@ -243,8 +250,7 @@ __create_vma(struct tiled_blits *t, size_t size, bool lmem)
 		return ERR_CAST(obj);
 
 	vma = i915_vma_instance(obj, t->ce->vm, NULL);
-	if (IS_ERR(vma))
-		i915_gem_object_put(obj);
+	i915_gem_object_put(obj);
 
 	return vma;
 }
diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_coherency.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_coherency.c
index b8dd6fabe70a..34f2d873ca83 100644
--- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_coherency.c
+++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_coherency.c
@@ -101,7 +101,6 @@ static int gtt_set(struct context *ctx, unsigned long offset, u32 v)
 	intel_gt_pm_get(vma->vm->gt);
 
 	map = i915_vma_pin_iomap(vma);
-	i915_vma_unpin(vma);
 	if (IS_ERR(map)) {
 		err = PTR_ERR(map);
 		goto out_rpm;
@@ -112,6 +111,7 @@ static int gtt_set(struct context *ctx, unsigned long offset, u32 v)
 
 out_rpm:
 	intel_gt_pm_put(vma->vm->gt);
+	i915_ggtt_unpin(vma);
 	return err;
 }
 
@@ -134,7 +134,6 @@ static int gtt_get(struct context *ctx, unsigned long offset, u32 *v)
 	intel_gt_pm_get(vma->vm->gt);
 
 	map = i915_vma_pin_iomap(vma);
-	i915_vma_unpin(vma);
 	if (IS_ERR(map)) {
 		err = PTR_ERR(map);
 		goto out_rpm;
@@ -145,6 +144,7 @@ static int gtt_get(struct context *ctx, unsigned long offset, u32 *v)
 
 out_rpm:
 	intel_gt_pm_put(vma->vm->gt);
+	i915_ggtt_unpin(vma);
 	return err;
 }
 
@@ -211,14 +211,14 @@ static int gpu_set(struct context *ctx, unsigned long offset, u32 v)
 
 	rq = intel_engine_create_kernel_request(ctx->engine);
 	if (IS_ERR(rq)) {
-		i915_vma_unpin(vma);
+		i915_ggtt_unpin(vma);
 		return PTR_ERR(rq);
 	}
 
 	cs = intel_ring_begin(rq, 4);
 	if (IS_ERR(cs)) {
 		i915_request_add(rq);
-		i915_vma_unpin(vma);
+		i915_ggtt_unpin(vma);
 		return PTR_ERR(cs);
 	}
 
@@ -245,9 +245,9 @@ static int gpu_set(struct context *ctx, unsigned long offset, u32 v)
 	if (err == 0)
 		err = i915_vma_move_to_active(vma, rq, EXEC_OBJECT_WRITE);
 	i915_vma_unlock(vma);
-	i915_vma_unpin(vma);
 
 	i915_request_add(rq);
+	i915_ggtt_unpin(vma);
 
 	return err;
 }
diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c
index c5d06af890bc..7be4cd6d6652 100644
--- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c
+++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c
@@ -435,7 +435,7 @@ static int gpu_fill(struct intel_context *ce,
 
 	err = i915_vma_pin(vma, 0, 0, PIN_HIGH | PIN_USER);
 	if (err)
-		return err;
+		goto out_vma;
 
 	/*
 	 * Within the GTT the huge objects maps every page onto
@@ -450,8 +450,10 @@ static int gpu_fill(struct intel_context *ce,
 			      (dw * sizeof(u32)),
 			      real_page_count(obj),
 			      dw);
-	i915_vma_unpin(vma);
 
+	i915_vma_unpin(vma);
+out_vma:
+	i915_vma_put(vma);
 	return err;
 }
 
@@ -924,10 +926,9 @@ static struct i915_vma *rpcs_query_batch(struct i915_vma *vma)
 	intel_gt_chipset_flush(vma->vm->gt);
 
 	vma = i915_vma_instance(obj, vma->vm, NULL);
-	if (IS_ERR(vma)) {
-		err = PTR_ERR(vma);
-		goto err;
-	}
+	i915_gem_object_put(obj);
+	if (IS_ERR(vma))
+		return vma;
 
 	err = i915_vma_pin(vma, 0, 0, PIN_USER);
 	if (err)
@@ -936,7 +937,7 @@ static struct i915_vma *rpcs_query_batch(struct i915_vma *vma)
 	return vma;
 
 err:
-	i915_gem_object_put(obj);
+	i915_vma_put(vma);
 	return ERR_PTR(err);
 }
 
@@ -960,16 +961,16 @@ emit_rpcs_query(struct drm_i915_gem_object *obj,
 	err = i915_gem_object_set_to_gtt_domain(obj, false);
 	i915_gem_object_unlock(obj);
 	if (err)
-		return err;
+		goto err_vma;
 
 	err = i915_vma_pin(vma, 0, 0, PIN_USER);
 	if (err)
-		return err;
+		goto err_vma;
 
 	batch = rpcs_query_batch(vma);
 	if (IS_ERR(batch)) {
 		err = PTR_ERR(batch);
-		goto err_vma;
+		goto err_unpin;
 	}
 
 	rq = i915_request_create(ce);
@@ -1007,7 +1008,7 @@ emit_rpcs_query(struct drm_i915_gem_object *obj,
 		goto skip_request;
 
 	i915_vma_unpin_and_release(&batch, 0);
-	i915_vma_unpin(vma);
+	i915_vma_unpin_and_release(&vma, 0);
 
 	*rq_out = i915_request_get(rq);
 
@@ -1020,9 +1021,10 @@ emit_rpcs_query(struct drm_i915_gem_object *obj,
 	i915_request_add(rq);
 err_batch:
 	i915_vma_unpin_and_release(&batch, 0);
-err_vma:
+err_unpin:
 	i915_vma_unpin(vma);
-
+err_vma:
+	i915_vma_put(vma);
 	return err;
 }
 
@@ -1544,7 +1546,7 @@ static int write_to_scratch(struct i915_gem_context *ctx,
 
 	err = i915_vma_pin(vma, 0, 0, PIN_USER | PIN_OFFSET_FIXED);
 	if (err)
-		goto out_vm;
+		goto out_vma;
 
 	rq = igt_request_alloc(ctx, engine);
 	if (IS_ERR(rq)) {
@@ -1574,12 +1576,14 @@ static int write_to_scratch(struct i915_gem_context *ctx,
 
 	i915_request_add(rq);
 
-	goto out_vm;
+	goto out_vma;
 skip_request:
 	i915_request_set_error_once(rq, err);
 	i915_request_add(rq);
 err_unpin:
 	i915_vma_unpin(vma);
+out_vma:
+	i915_vma_put(vma);
 out_vm:
 	i915_vm_put(vm);
 out:
@@ -1623,7 +1627,7 @@ static int read_from_scratch(struct i915_gem_context *ctx,
 
 		err = i915_vma_pin(vma, 0, 0, PIN_USER | PIN_OFFSET_FIXED);
 		if (err)
-			goto out_vm;
+			goto out_vma;
 
 		cmd = i915_gem_object_pin_map(obj, I915_MAP_WB);
 		if (IS_ERR(cmd)) {
@@ -1659,7 +1663,7 @@ static int read_from_scratch(struct i915_gem_context *ctx,
 
 		err = i915_vma_pin(vma, 0, 0, PIN_GLOBAL);
 		if (err)
-			goto out_vm;
+			goto out_vma;
 
 		cmd = i915_gem_object_pin_map(obj, I915_MAP_WB);
 		if (IS_ERR(cmd)) {
@@ -1716,23 +1720,25 @@ static int read_from_scratch(struct i915_gem_context *ctx,
 	err = i915_gem_object_set_to_cpu_domain(obj, false);
 	i915_gem_object_unlock(obj);
 	if (err)
-		goto out_vm;
+		goto out_vma;
 
 	cmd = i915_gem_object_pin_map(obj, I915_MAP_WB);
 	if (IS_ERR(cmd)) {
 		err = PTR_ERR(cmd);
-		goto out_vm;
+		goto out_vma;
 	}
 
 	*value = cmd[result / sizeof(*cmd)];
 	i915_gem_object_unpin_map(obj);
 
-	goto out_vm;
+	goto out_vma;
 skip_request:
 	i915_request_set_error_once(rq, err);
 	i915_request_add(rq);
 err_unpin:
 	i915_vma_unpin(vma);
+out_vma:
+	i915_vma_put(vma);
 out_vm:
 	i915_vm_put(vm);
 out:
diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_execbuffer.c
index daedff40236b..701ab4e0f45e 100644
--- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_execbuffer.c
@@ -188,7 +188,7 @@ static int igt_gpu_reloc(void *arg)
 
 		err = i915_vma_pin(ev.vma, 0, 0, PIN_USER | PIN_HIGH);
 		if (err)
-			goto err_unpin;
+			goto err_vma;
 
 		mutex_lock(&eb.context->timeline->mutex);
 		intel_context_enter(eb.context);
@@ -204,6 +204,8 @@ static int igt_gpu_reloc(void *arg)
 		intel_context_exit(eb.context);
 		mutex_unlock(&eb.context->timeline->mutex);
 		i915_vma_unpin(ev.vma);
+err_vma:
+		i915_vma_put(ev.vma);
 err_unpin:
 		intel_context_unpin(eb.context);
 err_put:
diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c
index 11f734fea3ab..7dc2afa6d599 100644
--- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c
+++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c
@@ -164,7 +164,7 @@ static int check_partial_mapping(struct drm_i915_gem_object *obj,
 	kunmap(p);
 
 out:
-	__i915_vma_put(vma);
+	i915_vma_put(vma);
 	return err;
 }
 
@@ -223,6 +223,7 @@ static int check_partial_mappings(struct drm_i915_gem_object *obj,
 		if (IS_ERR(io)) {
 			pr_err("Failed to iomap partial view: offset=%lu; err=%d\n",
 			       page, (int)PTR_ERR(io));
+			i915_vma_put(vma);
 			return PTR_ERR(io);
 		}
 
@@ -230,36 +231,35 @@ static int check_partial_mappings(struct drm_i915_gem_object *obj,
 		i915_vma_unpin_iomap(vma);
 
 		offset = tiled_offset(tile, page << PAGE_SHIFT);
-		if (offset >= obj->base.size)
-			continue;
-
-		intel_gt_flush_ggtt_writes(&to_i915(obj->base.dev)->gt);
-
-		p = i915_gem_object_get_page(obj, offset >> PAGE_SHIFT);
-		cpu = kmap(p) + offset_in_page(offset);
-		drm_clflush_virt_range(cpu, sizeof(*cpu));
-		if (*cpu != (u32)page) {
-			pr_err("Partial view for %lu [%u] (offset=%llu, size=%u [%llu, row size %u], fence=%d, tiling=%d, stride=%d) misalignment, expected write to page (%llu + %u [0x%llx]) of 0x%x, found 0x%x\n",
-			       page, n,
-			       view.partial.offset,
-			       view.partial.size,
-			       vma->size >> PAGE_SHIFT,
-			       tile->tiling ? tile_row_pages(obj) : 0,
-			       vma->fence ? vma->fence->id : -1, tile->tiling, tile->stride,
-			       offset >> PAGE_SHIFT,
-			       (unsigned int)offset_in_page(offset),
-			       offset,
-			       (u32)page, *cpu);
-			err = -EINVAL;
+		if (offset < obj->base.size) {
+			intel_gt_flush_ggtt_writes(&to_i915(obj->base.dev)->gt);
+
+			p = i915_gem_object_get_page(obj, offset >> PAGE_SHIFT);
+			cpu = kmap(p) + offset_in_page(offset);
+			drm_clflush_virt_range(cpu, sizeof(*cpu));
+			if (*cpu != (u32)page) {
+				pr_err("Partial view for %lu [%u] (offset=%llu, size=%u [%llu, row size %u], fence=%d, tiling=%d, stride=%d) misalignment, expected write to page (%llu + %u [0x%llx]) of 0x%x, found 0x%x\n",
+				       page, n,
+				       view.partial.offset,
+				       view.partial.size,
+				       vma->size >> PAGE_SHIFT,
+				       tile->tiling ? tile_row_pages(obj) : 0,
+				       vma->fence ? vma->fence->id : -1, tile->tiling, tile->stride,
+				       offset >> PAGE_SHIFT,
+				       (unsigned int)offset_in_page(offset),
+				       offset,
+				       (u32)page, *cpu);
+				err = -EINVAL;
+			}
+			*cpu = 0;
+			drm_clflush_virt_range(cpu, sizeof(*cpu));
+			kunmap(p);
 		}
-		*cpu = 0;
-		drm_clflush_virt_range(cpu, sizeof(*cpu));
-		kunmap(p);
+
+		i915_vma_put(vma);
 		if (err)
 			return err;
 
-		__i915_vma_put(vma);
-
 		if (igt_timeout(end_time,
 				"%s: timed out after tiling=%d stride=%d\n",
 				__func__, tile->tiling, tile->stride))
@@ -535,12 +535,15 @@ static int make_obj_busy(struct drm_i915_gem_object *obj)
 			return PTR_ERR(vma);
 
 		err = i915_vma_pin(vma, 0, 0, PIN_USER);
-		if (err)
+		if (err) {
+			i915_vma_put(vma);
 			return err;
+		}
 
 		rq = intel_engine_create_kernel_request(engine);
 		if (IS_ERR(rq)) {
 			i915_vma_unpin(vma);
+			i915_vma_put(vma);
 			return PTR_ERR(rq);
 		}
 
@@ -553,6 +556,7 @@ static int make_obj_busy(struct drm_i915_gem_object *obj)
 
 		i915_request_add(rq);
 		i915_vma_unpin(vma);
+		i915_vma_put(vma);
 		if (err)
 			return err;
 	}
@@ -738,7 +742,6 @@ static int gtt_set(struct drm_i915_gem_object *obj)
 
 	intel_gt_pm_get(vma->vm->gt);
 	map = i915_vma_pin_iomap(vma);
-	i915_vma_unpin(vma);
 	if (IS_ERR(map)) {
 		err = PTR_ERR(map);
 		goto out;
@@ -749,6 +752,7 @@ static int gtt_set(struct drm_i915_gem_object *obj)
 
 out:
 	intel_gt_pm_put(vma->vm->gt);
+	i915_ggtt_unpin(vma);
 	return err;
 }
 
@@ -764,7 +768,6 @@ static int gtt_check(struct drm_i915_gem_object *obj)
 
 	intel_gt_pm_get(vma->vm->gt);
 	map = i915_vma_pin_iomap(vma);
-	i915_vma_unpin(vma);
 	if (IS_ERR(map)) {
 		err = PTR_ERR(map);
 		goto out;
@@ -779,6 +782,7 @@ static int gtt_check(struct drm_i915_gem_object *obj)
 
 out:
 	intel_gt_pm_put(vma->vm->gt);
+	i915_ggtt_unpin(vma);
 	return err;
 }
 
@@ -1132,7 +1136,7 @@ static int __igt_mmap_gpu(struct drm_i915_private *i915,
 
 		err = i915_vma_pin(vma, 0, 0, PIN_USER);
 		if (err)
-			goto out_unmap;
+			goto out_vma;
 
 		rq = i915_request_create(engine->kernel_context);
 		if (IS_ERR(rq)) {
@@ -1166,6 +1170,8 @@ static int __igt_mmap_gpu(struct drm_i915_private *i915,
 
 out_unpin:
 		i915_vma_unpin(vma);
+out_vma:
+		i915_vma_put(vma);
 		if (err)
 			goto out_unmap;
 	}
diff --git a/drivers/gpu/drm/i915/gem/selftests/igt_gem_utils.c b/drivers/gpu/drm/i915/gem/selftests/igt_gem_utils.c
index e21b5023ca7d..c5b2c62a56fe 100644
--- a/drivers/gpu/drm/i915/gem/selftests/igt_gem_utils.c
+++ b/drivers/gpu/drm/i915/gem/selftests/igt_gem_utils.c
@@ -56,8 +56,8 @@ igt_emit_store_dw(struct i915_vma *vma,
 
 	cmd = i915_gem_object_pin_map(obj, I915_MAP_WC);
 	if (IS_ERR(cmd)) {
-		err = PTR_ERR(cmd);
-		goto err;
+		i915_gem_object_put(obj);
+		return ERR_CAST(cmd);
 	}
 
 	GEM_BUG_ON(offset + (count - 1) * PAGE_SIZE > vma->node.size);
@@ -90,19 +90,18 @@ igt_emit_store_dw(struct i915_vma *vma,
 	intel_gt_chipset_flush(vma->vm->gt);
 
 	vma = i915_vma_instance(obj, vma->vm, NULL);
-	if (IS_ERR(vma)) {
-		err = PTR_ERR(vma);
-		goto err;
-	}
+	i915_gem_object_put(obj);
+	if (IS_ERR(vma))
+		return vma;
 
 	err = i915_vma_pin(vma, 0, 0, PIN_USER);
 	if (err)
-		goto err;
+		goto err_vma;
 
 	return vma;
 
-err:
-	i915_gem_object_put(obj);
+err_vma:
+	i915_vma_put(vma);
 	return ERR_PTR(err);
 }
 
diff --git a/drivers/gpu/drm/i915/gt/gen6_ppgtt.c b/drivers/gpu/drm/i915/gt/gen6_ppgtt.c
index ea9a8044fd56..d557bfb832fb 100644
--- a/drivers/gpu/drm/i915/gt/gen6_ppgtt.c
+++ b/drivers/gpu/drm/i915/gt/gen6_ppgtt.c
@@ -266,7 +266,7 @@ static void gen6_ppgtt_cleanup(struct i915_address_space *vm)
 {
 	struct gen6_ppgtt *ppgtt = to_gen6_ppgtt(i915_vm_to_ppgtt(vm));
 
-	__i915_vma_put(ppgtt->vma);
+	i915_vma_put(ppgtt->vma);
 
 	gen6_ppgtt_free_pd(ppgtt);
 	free_scratch(vm);
diff --git a/drivers/gpu/drm/i915/gt/intel_engine_cs.c b/drivers/gpu/drm/i915/gt/intel_engine_cs.c
index 7bf2f76212f0..6e5268d83962 100644
--- a/drivers/gpu/drm/i915/gt/intel_engine_cs.c
+++ b/drivers/gpu/drm/i915/gt/intel_engine_cs.c
@@ -535,7 +535,7 @@ static void cleanup_status_page(struct intel_engine_cs *engine)
 		i915_vma_unpin(vma);
 
 	i915_gem_object_unpin_map(vma->obj);
-	i915_gem_object_put(vma->obj);
+	i915_vma_put(vma);
 }
 
 static int pin_ggtt_status_page(struct intel_engine_cs *engine,
@@ -586,10 +586,9 @@ static int init_status_page(struct intel_engine_cs *engine)
 	i915_gem_object_set_cache_coherency(obj, I915_CACHE_LLC);
 
 	vma = i915_vma_instance(obj, &engine->gt->ggtt->vm, NULL);
-	if (IS_ERR(vma)) {
-		ret = PTR_ERR(vma);
-		goto err;
-	}
+	i915_gem_object_put(obj);
+	if (IS_ERR(vma))
+		return PTR_ERR(vma);
 
 	vaddr = i915_gem_object_pin_map(obj, I915_MAP_WB);
 	if (IS_ERR(vaddr)) {
@@ -611,7 +610,7 @@ static int init_status_page(struct intel_engine_cs *engine)
 err_unpin:
 	i915_gem_object_unpin_map(obj);
 err:
-	i915_gem_object_put(obj);
+	i915_vma_put(vma);
 	return ret;
 }
 
diff --git a/drivers/gpu/drm/i915/gt/intel_ggtt.c b/drivers/gpu/drm/i915/gt/intel_ggtt.c
index 59a4a3ab6bfd..916c61914526 100644
--- a/drivers/gpu/drm/i915/gt/intel_ggtt.c
+++ b/drivers/gpu/drm/i915/gt/intel_ggtt.c
@@ -684,6 +684,7 @@ static void ggtt_cleanup_hw(struct i915_ggtt *ggtt)
 
 	rcu_barrier(); /* flush the RCU'ed__i915_vm_release */
 	flush_workqueue(ggtt->vm.i915->wq);
+	flush_work(&ggtt->vm.release.work);
 
 	mutex_lock(&ggtt->vm.mutex);
 
diff --git a/drivers/gpu/drm/i915/gt/intel_gt.c b/drivers/gpu/drm/i915/gt/intel_gt.c
index ebc29b6ee86c..9a2076dbaafd 100644
--- a/drivers/gpu/drm/i915/gt/intel_gt.c
+++ b/drivers/gpu/drm/i915/gt/intel_gt.c
@@ -26,9 +26,6 @@ void intel_gt_init_early(struct intel_gt *gt, struct drm_i915_private *i915)
 
 	spin_lock_init(&gt->irq_lock);
 
-	INIT_LIST_HEAD(&gt->closed_vma);
-	spin_lock_init(&gt->closed_lock);
-
 	intel_gt_init_buffer_pool(gt);
 	intel_gt_init_reset(gt);
 	intel_gt_init_requests(gt);
@@ -37,6 +34,8 @@ void intel_gt_init_early(struct intel_gt *gt, struct drm_i915_private *i915)
 
 	intel_rps_init_early(&gt->rps);
 	intel_uc_init_early(&gt->uc);
+
+	i915_vma_clock_init_early(&gt->vma_clock);
 }
 
 void intel_gt_init_hw_early(struct intel_gt *gt, struct i915_ggtt *ggtt)
@@ -332,7 +331,7 @@ static int intel_gt_init_scratch(struct intel_gt *gt, unsigned int size)
 	struct drm_i915_private *i915 = gt->i915;
 	struct drm_i915_gem_object *obj;
 	struct i915_vma *vma;
-	int ret;
+	int err;
 
 	obj = i915_gem_object_create_stolen(i915, size);
 	if (IS_ERR(obj))
@@ -343,22 +342,18 @@ static int intel_gt_init_scratch(struct intel_gt *gt, unsigned int size)
 	}
 
 	vma = i915_vma_instance(obj, &gt->ggtt->vm, NULL);
-	if (IS_ERR(vma)) {
-		ret = PTR_ERR(vma);
-		goto err_unref;
-	}
+	i915_gem_object_put(obj);
+	if (IS_ERR(vma))
+		return PTR_ERR(vma);
 
-	ret = i915_ggtt_pin(vma, 0, PIN_HIGH);
-	if (ret)
-		goto err_unref;
+	err = i915_ggtt_pin(vma, 0, PIN_HIGH);
+	if (err) {
+		i915_vma_put(vma);
+		return err;
+	}
 
 	gt->scratch = i915_vma_make_unshrinkable(vma);
-
 	return 0;
-
-err_unref:
-	i915_gem_object_put(obj);
-	return ret;
 }
 
 static void intel_gt_fini_scratch(struct intel_gt *gt)
@@ -595,6 +590,7 @@ int intel_gt_init(struct intel_gt *gt)
 void intel_gt_driver_remove(struct intel_gt *gt)
 {
 	__intel_gt_disable(gt);
+	i915_vma_clock_flush(&gt->vma_clock);
 
 	intel_uc_driver_remove(&gt->uc);
 
@@ -633,6 +629,8 @@ void intel_gt_driver_release(struct intel_gt *gt)
 
 void intel_gt_driver_late_release(struct intel_gt *gt)
 {
+	i915_vma_clock_flush(&gt->vma_clock);
+
 	/* We need to wait for inflight RCU frees to release their grip */
 	rcu_barrier();
 
diff --git a/drivers/gpu/drm/i915/gt/intel_gt_pm.c b/drivers/gpu/drm/i915/gt/intel_gt_pm.c
index f1d5333f9456..4d253efc62f0 100644
--- a/drivers/gpu/drm/i915/gt/intel_gt_pm.c
+++ b/drivers/gpu/drm/i915/gt/intel_gt_pm.c
@@ -81,7 +81,6 @@ static int __gt_park(struct intel_wakeref *wf)
 
 	intel_gt_park_requests(gt);
 
-	i915_vma_parked(gt);
 	i915_pmu_gt_parked(i915);
 	intel_rps_park(&gt->rps);
 	intel_rc6_park(&gt->rc6);
diff --git a/drivers/gpu/drm/i915/gt/intel_gt_types.h b/drivers/gpu/drm/i915/gt/intel_gt_types.h
index 0cc1d6b185dc..1681e952d186 100644
--- a/drivers/gpu/drm/i915/gt/intel_gt_types.h
+++ b/drivers/gpu/drm/i915/gt/intel_gt_types.h
@@ -59,9 +59,6 @@ struct intel_gt {
 	struct intel_wakeref wakeref;
 	atomic_t user_wakeref;
 
-	struct list_head closed_vma;
-	spinlock_t closed_lock; /* guards the list of closed_vma */
-
 	ktime_t last_init_time;
 	struct intel_reset reset;
 
@@ -108,6 +105,8 @@ struct intel_gt {
 	 */
 	struct intel_gt_buffer_pool buffer_pool;
 
+	struct i915_vma_clock vma_clock;
+
 	struct i915_vma *scratch;
 };
 
diff --git a/drivers/gpu/drm/i915/gt/intel_gtt.c b/drivers/gpu/drm/i915/gt/intel_gtt.c
index 8c044e6b4880..4b4adf2d9f3f 100644
--- a/drivers/gpu/drm/i915/gt/intel_gtt.c
+++ b/drivers/gpu/drm/i915/gt/intel_gtt.c
@@ -28,33 +28,10 @@ int pin_pt_dma(struct i915_address_space *vm, struct drm_i915_gem_object *obj)
 	return 0;
 }
 
-void __i915_vm_close(struct i915_address_space *vm)
-{
-	struct i915_vma *vma, *vn;
-
-	if (!atomic_dec_and_mutex_lock(&vm->open, &vm->mutex))
-		return;
-
-	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_put(vma);
-
-		i915_gem_object_put(obj);
-	}
-	GEM_BUG_ON(!list_empty(&vm->bound_list));
-
-	mutex_unlock(&vm->mutex);
-}
-
 void i915_address_space_fini(struct i915_address_space *vm)
 {
+	GEM_BUG_ON(work_pending(&vm->release.work));
+
 	i915_active_fini(&vm->binding);
 
 	drm_mm_takedown(&vm->mm);
@@ -83,12 +60,76 @@ void i915_vm_release(struct kref *kref)
 	queue_rcu_work(vm->i915->wq, &vm->rcu);
 }
 
+static void vma_decouple_obj(struct i915_vma *vma)
+{
+	struct drm_i915_gem_object *obj;
+
+	obj = fetch_and_zero(&vma->obj);
+	if (!obj)
+		return;
+
+	spin_lock(&obj->vma.lock);
+	list_del(&vma->obj_link);
+	if (!RB_EMPTY_NODE(&vma->obj_node))
+		rb_erase(&vma->obj_node, &obj->vma.tree);
+	spin_unlock(&obj->vma.lock);
+
+	i915_gem_object_put(obj);
+}
+
+static void i915_vm_release_vma(struct work_struct *wrk)
+{
+	struct i915_address_space *vm =
+		container_of(wrk, typeof(*vm), release.work);
+	struct i915_vma *vma, *next;
+	LIST_HEAD(list);
+
+	spin_lock_irq(&vm->release.lock);
+	list_splice_init(&vm->release.list, &list);
+	spin_unlock_irq(&vm->release.lock);
+
+	mutex_lock(&vm->mutex);
+	list_for_each_entry(vma, &list, closed_link) {
+		if (drm_mm_node_allocated(&vma->node)) {
+			atomic_and(~I915_VMA_PIN_MASK, &vma->flags);
+			__i915_vma_unbind(vma);
+			GEM_BUG_ON(drm_mm_node_allocated(&vma->node));
+		}
+	}
+	mutex_unlock(&vm->mutex);
+
+	list_for_each_entry_safe(vma, next, &list, closed_link) {
+		vma_decouple_obj(vma);
+		i915_vma_release(&vma->ref);
+	}
+}
+
 void i915_address_space_init(struct i915_address_space *vm, int subclass)
 {
 	kref_init(&vm->ref);
 	INIT_RCU_WORK(&vm->rcu, __i915_vm_release);
 	atomic_set(&vm->open, 1);
 
+	spin_lock_init(&vm->release.lock);
+	INIT_LIST_HEAD(&vm->release.list);
+	INIT_WORK(&vm->release.work, i915_vm_release_vma);
+#if IS_ENABLED(CONFIG_LOCKDEP)
+	/* XXX lockdep_set_subclass(&vm->release.work, subclass); */
+	lockdep_init_map_waits(&vm->release.work.lockdep_map,
+			       vm->release.work.lockdep_map.name,
+			       vm->release.work.lockdep_map.key,
+			       subclass,
+			       vm->release.work.lockdep_map.wait_type_inner,
+			       vm->release.work.lockdep_map.wait_type_outer);
+	/*
+	 * Due to an interesting quirk in lockdep's internal debug tracking,
+	 * after setting a subclass we must ensure the lock is used. Otherwise,
+	 * nr_unused_locks is incremented once too often.
+	 */
+	lock_map_acquire(&vm->release.work.lockdep_map);
+	lock_map_release(&vm->release.work.lockdep_map);
+#endif
+
 	/*
 	 * The vm->mutex must be reclaim safe (for use in the shrinker).
 	 * Do a dummy acquire now under fs_reclaim so that any allocation
diff --git a/drivers/gpu/drm/i915/gt/intel_gtt.h b/drivers/gpu/drm/i915/gt/intel_gtt.h
index a4809f4ffe53..6e19d4dc356a 100644
--- a/drivers/gpu/drm/i915/gt/intel_gtt.h
+++ b/drivers/gpu/drm/i915/gt/intel_gtt.h
@@ -250,6 +250,12 @@ struct i915_address_space {
 
 	struct i915_active binding;
 
+	struct {
+		spinlock_t lock;
+		struct list_head list;
+		struct work_struct work;
+	} release;
+
 	/* Global GTT */
 	bool is_ggtt:1;
 
@@ -401,23 +407,11 @@ i915_vm_open(struct i915_address_space *vm)
 	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));
-	__i915_vm_close(vm);
-
+	atomic_dec(&vm->open);
 	i915_vm_put(vm);
 }
 
diff --git a/drivers/gpu/drm/i915/gt/intel_lrc.c b/drivers/gpu/drm/i915/gt/intel_lrc.c
index e866b8d721ed..940a8428327f 100644
--- a/drivers/gpu/drm/i915/gt/intel_lrc.c
+++ b/drivers/gpu/drm/i915/gt/intel_lrc.c
@@ -3850,21 +3850,18 @@ static int lrc_setup_wa_ctx(struct intel_engine_cs *engine)
 		return PTR_ERR(obj);
 
 	vma = i915_vma_instance(obj, &engine->gt->ggtt->vm, NULL);
-	if (IS_ERR(vma)) {
-		err = PTR_ERR(vma);
-		goto err;
-	}
+	i915_gem_object_put(obj);
+	if (IS_ERR(vma))
+		return PTR_ERR(vma);
 
 	err = i915_ggtt_pin(vma, 0, PIN_HIGH);
-	if (err)
-		goto err;
+	if (err) {
+		i915_vma_put(vma);
+		return err;
+	}
 
 	engine->wa_ctx.vma = vma;
 	return 0;
-
-err:
-	i915_gem_object_put(obj);
-	return err;
 }
 
 static void lrc_destroy_wa_ctx(struct intel_engine_cs *engine)
@@ -5291,10 +5288,9 @@ static int __execlists_context_alloc(struct intel_context *ce,
 		return PTR_ERR(ctx_obj);
 
 	vma = i915_vma_instance(ctx_obj, &engine->gt->ggtt->vm, NULL);
-	if (IS_ERR(vma)) {
-		ret = PTR_ERR(vma);
-		goto error_deref_obj;
-	}
+	i915_gem_object_put(ctx_obj);
+	if (IS_ERR(vma))
+		return PTR_ERR(vma);
 
 	if (!ce->timeline) {
 		struct intel_timeline *tl;
@@ -5311,7 +5307,7 @@ static int __execlists_context_alloc(struct intel_context *ce,
 		tl = intel_timeline_create(engine->gt, hwsp);
 		if (IS_ERR(tl)) {
 			ret = PTR_ERR(tl);
-			goto error_deref_obj;
+			goto err_vma;
 		}
 
 		ce->timeline = tl;
@@ -5320,14 +5316,14 @@ static int __execlists_context_alloc(struct intel_context *ce,
 	ring = intel_engine_create_ring(engine, (unsigned long)ce->ring);
 	if (IS_ERR(ring)) {
 		ret = PTR_ERR(ring);
-		goto error_deref_obj;
+		goto err_vma;
 	}
 
 	ret = populate_lr_context(ce, ctx_obj, engine, ring);
 	if (ret) {
 		drm_dbg(&engine->i915->drm,
 			"Failed to populate LRC: %d\n", ret);
-		goto error_ring_free;
+		goto err_ring;
 	}
 
 	ce->ring = ring;
@@ -5335,10 +5331,10 @@ static int __execlists_context_alloc(struct intel_context *ce,
 
 	return 0;
 
-error_ring_free:
+err_ring:
 	intel_ring_put(ring);
-error_deref_obj:
-	i915_gem_object_put(ctx_obj);
+err_vma:
+	i915_vma_put(vma);
 	return ret;
 }
 
diff --git a/drivers/gpu/drm/i915/gt/intel_renderstate.c b/drivers/gpu/drm/i915/gt/intel_renderstate.c
index 6db23389e427..24927e9bcad7 100644
--- a/drivers/gpu/drm/i915/gt/intel_renderstate.c
+++ b/drivers/gpu/drm/i915/gt/intel_renderstate.c
@@ -176,14 +176,13 @@ int intel_renderstate_init(struct intel_renderstate *so,
 		return PTR_ERR(obj);
 
 	so->vma = i915_vma_instance(obj, &engine->gt->ggtt->vm, NULL);
-	if (IS_ERR(so->vma)) {
-		err = PTR_ERR(so->vma);
-		goto err_obj;
-	}
+	i915_gem_object_put(obj);
+	if (IS_ERR(so->vma))
+		return PTR_ERR(so->vma);
 
 	err = i915_vma_pin(so->vma, 0, 0, PIN_GLOBAL | PIN_HIGH);
 	if (err)
-		goto err_obj;
+		goto err_vma;
 
 	err = render_state_setup(so, engine->i915);
 	if (err)
@@ -193,8 +192,8 @@ int intel_renderstate_init(struct intel_renderstate *so,
 
 err_unpin:
 	i915_vma_unpin(so->vma);
-err_obj:
-	i915_gem_object_put(obj);
+err_vma:
+	i915_vma_put(so->vma);
 	so->vma = NULL;
 	return err;
 }
diff --git a/drivers/gpu/drm/i915/gt/intel_ring.c b/drivers/gpu/drm/i915/gt/intel_ring.c
index bdb324167ef3..fc78f9e2f7d3 100644
--- a/drivers/gpu/drm/i915/gt/intel_ring.c
+++ b/drivers/gpu/drm/i915/gt/intel_ring.c
@@ -117,13 +117,8 @@ static struct i915_vma *create_ring_vma(struct i915_ggtt *ggtt, int size)
 		i915_gem_object_set_readonly(obj);
 
 	vma = i915_vma_instance(obj, vm, NULL);
-	if (IS_ERR(vma))
-		goto err;
-
-	return vma;
-
-err:
 	i915_gem_object_put(obj);
+
 	return vma;
 }
 
diff --git a/drivers/gpu/drm/i915/gt/intel_ring_submission.c b/drivers/gpu/drm/i915/gt/intel_ring_submission.c
index f1f27b7fc746..9ccc7c742e99 100644
--- a/drivers/gpu/drm/i915/gt/intel_ring_submission.c
+++ b/drivers/gpu/drm/i915/gt/intel_ring_submission.c
@@ -508,7 +508,6 @@ alloc_context_vma(struct intel_engine_cs *engine)
 	struct drm_i915_private *i915 = engine->i915;
 	struct drm_i915_gem_object *obj;
 	struct i915_vma *vma;
-	int err;
 
 	obj = i915_gem_object_create_shmem(i915, engine->context_size);
 	if (IS_ERR(obj))
@@ -537,8 +536,8 @@ alloc_context_vma(struct intel_engine_cs *engine)
 
 		vaddr = i915_gem_object_pin_map(obj, I915_MAP_WB);
 		if (IS_ERR(vaddr)) {
-			err = PTR_ERR(vaddr);
-			goto err_obj;
+			i915_gem_object_put(obj);
+			return ERR_CAST(vaddr);
 		}
 
 		shmem_read(engine->default_state, 0,
@@ -549,16 +548,9 @@ alloc_context_vma(struct intel_engine_cs *engine)
 	}
 
 	vma = i915_vma_instance(obj, &engine->gt->ggtt->vm, NULL);
-	if (IS_ERR(vma)) {
-		err = PTR_ERR(vma);
-		goto err_obj;
-	}
+	i915_gem_object_put(obj);
 
 	return vma;
-
-err_obj:
-	i915_gem_object_put(obj);
-	return ERR_PTR(err);
 }
 
 static int ring_context_alloc(struct intel_context *ce)
@@ -1191,15 +1183,14 @@ static int gen7_ctx_switch_bb_init(struct intel_engine_cs *engine)
 		return PTR_ERR(obj);
 
 	vma = i915_vma_instance(obj, engine->gt->vm, NULL);
-	if (IS_ERR(vma)) {
-		err = PTR_ERR(vma);
-		goto err_obj;
-	}
+	i915_gem_object_put(obj);
+	if (IS_ERR(vma))
+		return PTR_ERR(vma);
 
 	vma->private = intel_context_create(engine); /* dummy residuals */
 	if (IS_ERR(vma->private)) {
 		err = PTR_ERR(vma->private);
-		goto err_obj;
+		goto err_vma;
 	}
 
 	err = i915_vma_pin(vma, 0, 0, PIN_USER | PIN_HIGH);
@@ -1221,8 +1212,8 @@ static int gen7_ctx_switch_bb_init(struct intel_engine_cs *engine)
 	i915_vma_unpin(vma);
 err_private:
 	intel_context_put(vma->private);
-err_obj:
-	i915_gem_object_put(obj);
+err_vma:
+	i915_vma_put(vma);
 	return err;
 }
 
diff --git a/drivers/gpu/drm/i915/gt/intel_timeline.c b/drivers/gpu/drm/i915/gt/intel_timeline.c
index e4a5326633b8..512d63549cd5 100644
--- a/drivers/gpu/drm/i915/gt/intel_timeline.c
+++ b/drivers/gpu/drm/i915/gt/intel_timeline.c
@@ -39,8 +39,7 @@ static struct i915_vma *__hwsp_alloc(struct intel_gt *gt)
 	i915_gem_object_set_cache_coherency(obj, I915_CACHE_LLC);
 
 	vma = i915_vma_instance(obj, &gt->ggtt->vm, NULL);
-	if (IS_ERR(vma))
-		i915_gem_object_put(obj);
+	i915_gem_object_put(obj);
 
 	return vma;
 }
diff --git a/drivers/gpu/drm/i915/gt/intel_workarounds.c b/drivers/gpu/drm/i915/gt/intel_workarounds.c
index 2da366821dda..2d90bc9106b5 100644
--- a/drivers/gpu/drm/i915/gt/intel_workarounds.c
+++ b/drivers/gpu/drm/i915/gt/intel_workarounds.c
@@ -1947,21 +1947,19 @@ create_scratch(struct i915_address_space *vm, int count)
 	i915_gem_object_set_cache_coherency(obj, I915_CACHE_LLC);
 
 	vma = i915_vma_instance(obj, vm, NULL);
+	i915_gem_object_put(obj);
 	if (IS_ERR(vma)) {
-		err = PTR_ERR(vma);
-		goto err_obj;
+		return vma;
 	}
 
 	err = i915_vma_pin(vma, 0, 0,
 			   i915_vma_is_ggtt(vma) ? PIN_GLOBAL : PIN_USER);
-	if (err)
-		goto err_obj;
+	if (err) {
+		i915_vma_put(vma);
+		return ERR_PTR(err);
+	}
 
 	return vma;
-
-err_obj:
-	i915_gem_object_put(obj);
-	return ERR_PTR(err);
 }
 
 static const struct {
diff --git a/drivers/gpu/drm/i915/gt/selftest_engine_cs.c b/drivers/gpu/drm/i915/gt/selftest_engine_cs.c
index 729c3c7b11e2..438844eb3600 100644
--- a/drivers/gpu/drm/i915/gt/selftest_engine_cs.c
+++ b/drivers/gpu/drm/i915/gt/selftest_engine_cs.c
@@ -74,8 +74,8 @@ static struct i915_vma *create_empty_batch(struct intel_context *ce)
 
 	cs = i915_gem_object_pin_map(obj, I915_MAP_WB);
 	if (IS_ERR(cs)) {
-		err = PTR_ERR(cs);
-		goto err_put;
+		i915_gem_object_put(obj);
+		return ERR_CAST(cs);
 	}
 
 	cs[0] = MI_BATCH_BUFFER_END;
@@ -83,23 +83,18 @@ static struct i915_vma *create_empty_batch(struct intel_context *ce)
 	i915_gem_object_flush_map(obj);
 
 	vma = i915_vma_instance(obj, ce->vm, NULL);
-	if (IS_ERR(vma)) {
-		err = PTR_ERR(vma);
-		goto err_unpin;
-	}
+	i915_gem_object_put(obj);
+	if (IS_ERR(vma))
+		return vma;
 
 	err = i915_vma_pin(vma, 0, 0, PIN_USER);
-	if (err)
-		goto err_unpin;
+	if (err) {
+		i915_vma_put(vma);
+		return ERR_PTR(err);
+	}
 
 	i915_gem_object_unpin_map(obj);
 	return vma;
-
-err_unpin:
-	i915_gem_object_unpin_map(obj);
-err_put:
-	i915_gem_object_put(obj);
-	return ERR_PTR(err);
 }
 
 static u32 trifilter(u32 *a)
@@ -209,10 +204,8 @@ static struct i915_vma *create_nop_batch(struct intel_context *ce)
 		return ERR_CAST(obj);
 
 	cs = i915_gem_object_pin_map(obj, I915_MAP_WB);
-	if (IS_ERR(cs)) {
-		err = PTR_ERR(cs);
-		goto err_put;
-	}
+	if (IS_ERR(cs))
+		return ERR_CAST(cs);
 
 	memset(cs, 0, SZ_64K);
 	cs[SZ_64K / sizeof(*cs) - 1] = MI_BATCH_BUFFER_END;
@@ -220,23 +213,18 @@ static struct i915_vma *create_nop_batch(struct intel_context *ce)
 	i915_gem_object_flush_map(obj);
 
 	vma = i915_vma_instance(obj, ce->vm, NULL);
-	if (IS_ERR(vma)) {
-		err = PTR_ERR(vma);
-		goto err_unpin;
-	}
+	i915_gem_object_put(obj);
+	if (IS_ERR(vma))
+		return vma;
 
 	err = i915_vma_pin(vma, 0, 0, PIN_USER);
-	if (err)
-		goto err_unpin;
+	if (err) {
+		i915_vma_put(vma);
+		return ERR_PTR(err);
+	}
 
 	i915_gem_object_unpin_map(obj);
 	return vma;
-
-err_unpin:
-	i915_gem_object_unpin_map(obj);
-err_put:
-	i915_gem_object_put(obj);
-	return ERR_PTR(err);
 }
 
 static int perf_mi_noop(void *arg)
diff --git a/drivers/gpu/drm/i915/gt/selftest_hangcheck.c b/drivers/gpu/drm/i915/gt/selftest_hangcheck.c
index fb5ebf930ab2..345144f4b3d0 100644
--- a/drivers/gpu/drm/i915/gt/selftest_hangcheck.c
+++ b/drivers/gpu/drm/i915/gt/selftest_hangcheck.c
@@ -145,15 +145,15 @@ hang_create_request(struct hang *h, struct intel_engine_cs *engine)
 
 	obj = i915_gem_object_create_internal(gt->i915, PAGE_SIZE);
 	if (IS_ERR(obj)) {
-		i915_vm_put(vm);
-		return ERR_CAST(obj);
+		err = PTR_ERR(obj);
+		goto put_vm;
 	}
 
 	vaddr = i915_gem_object_pin_map(obj, i915_coherent_map_type(gt->i915));
 	if (IS_ERR(vaddr)) {
 		i915_gem_object_put(obj);
-		i915_vm_put(vm);
-		return ERR_CAST(vaddr);
+		err = PTR_ERR(vaddr);
+		goto put_vm;
 	}
 
 	i915_gem_object_unpin_map(h->obj);
@@ -163,22 +163,18 @@ hang_create_request(struct hang *h, struct intel_engine_cs *engine)
 	h->batch = vaddr;
 
 	vma = i915_vma_instance(h->obj, vm, NULL);
-	if (IS_ERR(vma)) {
-		i915_vm_put(vm);
+	if (IS_ERR(vma))
 		return ERR_CAST(vma);
-	}
 
 	hws = i915_vma_instance(h->hws, vm, NULL);
 	if (IS_ERR(hws)) {
-		i915_vm_put(vm);
-		return ERR_CAST(hws);
+		err = PTR_ERR(hws);
+		goto put_vma;
 	}
 
 	err = i915_vma_pin(vma, 0, 0, PIN_USER);
-	if (err) {
-		i915_vm_put(vm);
-		return ERR_PTR(err);
-	}
+	if (err)
+		goto put_hws;
 
 	err = i915_vma_pin(hws, 0, 0, PIN_USER);
 	if (err)
@@ -276,6 +272,11 @@ hang_create_request(struct hang *h, struct intel_engine_cs *engine)
 	i915_vma_unpin(hws);
 unpin_vma:
 	i915_vma_unpin(vma);
+put_hws:
+	i915_vma_put(hws);
+put_vma:
+	i915_vma_put(vma);
+put_vm:
 	i915_vm_put(vm);
 	return err ? ERR_PTR(err) : rq;
 }
@@ -1191,20 +1192,22 @@ static int __igt_reset_evict_vma(struct intel_gt *gt,
 		err = i915_gem_object_set_tiling(obj, I915_TILING_X, 512);
 		if (err) {
 			pr_err("Invalid X-tiling settings; err:%d\n", err);
-			goto out_obj;
+			i915_gem_object_put(obj);
+			goto fini;
 		}
 	}
 
 	arg.vma = i915_vma_instance(obj, vm, NULL);
+	i915_gem_object_put(obj);
 	if (IS_ERR(arg.vma)) {
 		err = PTR_ERR(arg.vma);
-		goto out_obj;
+		goto fini;
 	}
 
 	rq = hang_create_request(&h, engine);
 	if (IS_ERR(rq)) {
 		err = PTR_ERR(rq);
-		goto out_obj;
+		goto out_vma;
 	}
 
 	pin_flags = i915_vma_is_ggtt(arg.vma) ? PIN_GLOBAL : PIN_USER;
@@ -1215,7 +1218,7 @@ static int __igt_reset_evict_vma(struct intel_gt *gt,
 	err = i915_vma_pin(arg.vma, 0, 0, pin_flags);
 	if (err) {
 		i915_request_add(rq);
-		goto out_obj;
+		goto out_vma;
 	}
 
 	if (flags & EXEC_OBJECT_NEEDS_FENCE) {
@@ -1224,7 +1227,7 @@ static int __igt_reset_evict_vma(struct intel_gt *gt,
 			pr_err("Unable to pin X-tiled fence; err:%d\n", err);
 			i915_vma_unpin(arg.vma);
 			i915_request_add(rq);
-			goto out_obj;
+			goto out_vma;
 		}
 	}
 
@@ -1294,8 +1297,8 @@ static int __igt_reset_evict_vma(struct intel_gt *gt,
 
 out_rq:
 	i915_request_put(rq);
-out_obj:
-	i915_gem_object_put(obj);
+out_vma:
+	i915_vma_put(arg.vma);
 fini:
 	hang_fini(&h);
 	if (intel_gt_is_wedged(gt))
diff --git a/drivers/gpu/drm/i915/gt/selftest_lrc.c b/drivers/gpu/drm/i915/gt/selftest_lrc.c
index daa4aabab9a7..dcdef9f0b780 100644
--- a/drivers/gpu/drm/i915/gt/selftest_lrc.c
+++ b/drivers/gpu/drm/i915/gt/selftest_lrc.c
@@ -38,14 +38,13 @@ static struct i915_vma *create_scratch(struct intel_gt *gt)
 	i915_gem_object_set_cache_coherency(obj, I915_CACHING_CACHED);
 
 	vma = i915_vma_instance(obj, &gt->ggtt->vm, NULL);
-	if (IS_ERR(vma)) {
-		i915_gem_object_put(obj);
+	i915_gem_object_put(obj);
+	if (IS_ERR(vma))
 		return vma;
-	}
 
 	err = i915_vma_pin(vma, 0, 0, PIN_GLOBAL);
 	if (err) {
-		i915_gem_object_put(obj);
+		i915_vma_put(vma);
 		return ERR_PTR(err);
 	}
 
@@ -1002,15 +1001,14 @@ static int live_timeslice_preempt(void *arg)
 		return PTR_ERR(obj);
 
 	vma = i915_vma_instance(obj, &gt->ggtt->vm, NULL);
-	if (IS_ERR(vma)) {
-		err = PTR_ERR(vma);
-		goto err_obj;
-	}
+	i915_gem_object_put(obj);
+	if (IS_ERR(vma))
+		return PTR_ERR(vma);
 
 	vaddr = i915_gem_object_pin_map(obj, I915_MAP_WC);
 	if (IS_ERR(vaddr)) {
 		err = PTR_ERR(vaddr);
-		goto err_obj;
+		goto err_vma;
 	}
 
 	err = i915_vma_pin(vma, 0, 0, PIN_GLOBAL);
@@ -1043,8 +1041,8 @@ static int live_timeslice_preempt(void *arg)
 	i915_vma_unpin(vma);
 err_map:
 	i915_gem_object_unpin_map(obj);
-err_obj:
-	i915_gem_object_put(obj);
+err_vma:
+	i915_vma_put(vma);
 	return err;
 }
 
@@ -1310,15 +1308,14 @@ static int live_timeslice_queue(void *arg)
 		return PTR_ERR(obj);
 
 	vma = i915_vma_instance(obj, &gt->ggtt->vm, NULL);
-	if (IS_ERR(vma)) {
-		err = PTR_ERR(vma);
-		goto err_obj;
-	}
+	i915_gem_object_put(obj);
+	if (IS_ERR(vma))
+		return PTR_ERR(vma);
 
 	vaddr = i915_gem_object_pin_map(obj, I915_MAP_WC);
 	if (IS_ERR(vaddr)) {
 		err = PTR_ERR(vaddr);
-		goto err_obj;
+		goto err_vma;
 	}
 
 	err = i915_vma_pin(vma, 0, 0, PIN_GLOBAL);
@@ -1408,8 +1405,8 @@ static int live_timeslice_queue(void *arg)
 	i915_vma_unpin(vma);
 err_map:
 	i915_gem_object_unpin_map(obj);
-err_obj:
-	i915_gem_object_put(obj);
+err_vma:
+	i915_vma_put(vma);
 	return err;
 }
 
@@ -1576,11 +1573,11 @@ static int live_busywait_preempt(void *arg)
 
 	err = i915_vma_pin(vma, 0, 0, PIN_GLOBAL);
 	if (err)
-		goto err_map;
+		goto err_vma;
 
 	err = i915_vma_sync(vma);
 	if (err)
-		goto err_vma;
+		goto err_unpin;
 
 	for_each_engine(engine, gt, id) {
 		struct i915_request *lo, *hi;
@@ -1702,8 +1699,10 @@ static int live_busywait_preempt(void *arg)
 	}
 
 	err = 0;
-err_vma:
+err_unpin:
 	i915_vma_unpin(vma);
+err_vma:
+	i915_vma_put(vma);
 err_map:
 	i915_gem_object_unpin_map(obj);
 err_obj:
@@ -2669,18 +2668,21 @@ static int create_gang(struct intel_engine_cs *engine,
 	}
 
 	vma = i915_vma_instance(obj, ce->vm, NULL);
+	i915_gem_object_put(obj);
 	if (IS_ERR(vma)) {
 		err = PTR_ERR(vma);
-		goto err_obj;
+		goto err_ce;
 	}
 
 	err = i915_vma_pin(vma, 0, 0, PIN_USER);
 	if (err)
-		goto err_obj;
+		goto err_vma;
 
 	cs = i915_gem_object_pin_map(obj, I915_MAP_WC);
-	if (IS_ERR(cs))
-		goto err_obj;
+	if (IS_ERR(cs)) {
+		i915_vma_unpin(vma);
+		goto err_vma;
+	}
 
 	/* Semaphore target: spin until zero */
 	*cs++ = MI_ARB_ON_OFF | MI_ARB_ENABLE;
@@ -2707,8 +2709,10 @@ static int create_gang(struct intel_engine_cs *engine,
 	i915_gem_object_unpin_map(obj);
 
 	rq = intel_context_create_request(ce);
-	if (IS_ERR(rq))
-		goto err_obj;
+	if (IS_ERR(rq)) {
+		i915_vma_unpin(vma);
+		goto err_vma;
+	}
 
 	rq->batch = i915_vma_get(vma);
 	i915_request_get(rq);
@@ -2726,7 +2730,7 @@ static int create_gang(struct intel_engine_cs *engine,
 	if (err)
 		goto err_rq;
 
-	i915_gem_object_put(obj);
+	i915_vma_put(vma);
 	intel_context_put(ce);
 
 	rq->client_link.next = &(*prev)->client_link;
@@ -2736,8 +2740,8 @@ static int create_gang(struct intel_engine_cs *engine,
 err_rq:
 	i915_vma_put(rq->batch);
 	i915_request_put(rq);
-err_obj:
-	i915_gem_object_put(obj);
+err_vma:
+	i915_vma_put(vma);
 err_ce:
 	intel_context_put(ce);
 	return err;
@@ -3015,10 +3019,9 @@ create_gpr_user(struct intel_engine_cs *engine,
 		return ERR_CAST(obj);
 
 	vma = i915_vma_instance(obj, result->vm, NULL);
-	if (IS_ERR(vma)) {
-		i915_gem_object_put(obj);
+	i915_gem_object_put(obj);
+	if (IS_ERR(vma))
 		return vma;
-	}
 
 	err = i915_vma_pin(vma, 0, 0, PIN_USER);
 	if (err) {
@@ -3085,10 +3088,9 @@ static struct i915_vma *create_global(struct intel_gt *gt, size_t sz)
 		return ERR_CAST(obj);
 
 	vma = i915_vma_instance(obj, &gt->ggtt->vm, NULL);
-	if (IS_ERR(vma)) {
-		i915_gem_object_put(obj);
+	i915_gem_object_put(obj);
+	if (IS_ERR(vma))
 		return vma;
-	}
 
 	err = i915_ggtt_pin(vma, 0, 0);
 	if (err) {
@@ -3121,12 +3123,12 @@ create_gpr_client(struct intel_engine_cs *engine,
 
 	err = i915_vma_pin(vma, 0, 0, PIN_USER);
 	if (err)
-		goto out_ce;
+		goto out_vma;
 
 	batch = create_gpr_user(engine, vma, offset);
 	if (IS_ERR(batch)) {
 		err = PTR_ERR(batch);
-		goto out_vma;
+		goto out_unpin;
 	}
 
 	rq = intel_context_create_request(ce);
@@ -3159,8 +3161,10 @@ create_gpr_client(struct intel_engine_cs *engine,
 
 out_batch:
 	i915_vma_put(batch);
-out_vma:
+out_unpin:
 	i915_vma_unpin(vma);
+out_vma:
+	i915_vma_put(vma);
 out_ce:
 	intel_context_put(ce);
 	return err ? ERR_PTR(err) : rq;
@@ -3467,8 +3471,10 @@ static int smoke_submit(struct preempt_smoke *smoke,
 			return PTR_ERR(vma);
 
 		err = i915_vma_pin(vma, 0, 0, PIN_USER);
-		if (err)
+		if (err) {
+			i915_vma_put(vma);
 			return err;
+		}
 	}
 
 	ctx->sched.priority = prio;
@@ -3494,8 +3500,10 @@ static int smoke_submit(struct preempt_smoke *smoke,
 	i915_request_add(rq);
 
 unpin:
-	if (vma)
+	if (vma) {
 		i915_vma_unpin(vma);
+		i915_vma_put(vma);
+	}
 
 	return err;
 }
@@ -5522,14 +5530,13 @@ create_user_vma(struct i915_address_space *vm, unsigned long size)
 		return ERR_CAST(obj);
 
 	vma = i915_vma_instance(obj, vm, NULL);
-	if (IS_ERR(vma)) {
-		i915_gem_object_put(obj);
+	i915_gem_object_put(obj);
+	if (IS_ERR(vma))
 		return vma;
-	}
 
 	err = i915_vma_pin(vma, 0, 0, PIN_USER);
 	if (err) {
-		i915_gem_object_put(obj);
+		i915_vma_put(vma);
 		return ERR_PTR(err);
 	}
 
diff --git a/drivers/gpu/drm/i915/gt/selftest_mocs.c b/drivers/gpu/drm/i915/gt/selftest_mocs.c
index b25eba50c88e..5779835a5a81 100644
--- a/drivers/gpu/drm/i915/gt/selftest_mocs.c
+++ b/drivers/gpu/drm/i915/gt/selftest_mocs.c
@@ -69,14 +69,13 @@ static struct i915_vma *create_scratch(struct intel_gt *gt)
 	i915_gem_object_set_cache_coherency(obj, I915_CACHING_CACHED);
 
 	vma = i915_vma_instance(obj, &gt->ggtt->vm, NULL);
-	if (IS_ERR(vma)) {
-		i915_gem_object_put(obj);
+	i915_gem_object_put(obj);
+	if (IS_ERR(vma))
 		return vma;
-	}
 
 	err = i915_vma_pin(vma, 0, 0, PIN_GLOBAL);
 	if (err) {
-		i915_gem_object_put(obj);
+		i915_vma_put(vma);
 		return ERR_PTR(err);
 	}
 
diff --git a/drivers/gpu/drm/i915/gt/selftest_ring_submission.c b/drivers/gpu/drm/i915/gt/selftest_ring_submission.c
index 3350e7c995bc..a72bd4ea8cd7 100644
--- a/drivers/gpu/drm/i915/gt/selftest_ring_submission.c
+++ b/drivers/gpu/drm/i915/gt/selftest_ring_submission.c
@@ -18,26 +18,25 @@ static struct i915_vma *create_wally(struct intel_engine_cs *engine)
 		return ERR_CAST(obj);
 
 	vma = i915_vma_instance(obj, engine->gt->vm, NULL);
-	if (IS_ERR(vma)) {
-		i915_gem_object_put(obj);
+	i915_gem_object_put(obj);
+	if (IS_ERR(vma))
 		return vma;
-	}
 
 	err = i915_vma_pin(vma, 0, 0, PIN_USER | PIN_HIGH);
 	if (err) {
-		i915_gem_object_put(obj);
+		i915_vma_put(vma);
 		return ERR_PTR(err);
 	}
 
 	err = i915_vma_sync(vma);
 	if (err) {
-		i915_gem_object_put(obj);
+		i915_vma_put(vma);
 		return ERR_PTR(err);
 	}
 
 	cs = i915_gem_object_pin_map(obj, I915_MAP_WC);
 	if (IS_ERR(cs)) {
-		i915_gem_object_put(obj);
+		i915_vma_put(vma);
 		return ERR_CAST(cs);
 	}
 
diff --git a/drivers/gpu/drm/i915/gt/selftest_rps.c b/drivers/gpu/drm/i915/gt/selftest_rps.c
index bb753f0c12eb..dbe8cd0e1522 100644
--- a/drivers/gpu/drm/i915/gt/selftest_rps.c
+++ b/drivers/gpu/drm/i915/gt/selftest_rps.c
@@ -76,10 +76,9 @@ create_spin_counter(struct intel_engine_cs *engine,
 	end = obj->base.size / sizeof(u32) - 1;
 
 	vma = i915_vma_instance(obj, vm, NULL);
-	if (IS_ERR(vma)) {
-		i915_gem_object_put(obj);
+	i915_gem_object_put(obj);
+	if (IS_ERR(vma))
 		return vma;
-	}
 
 	err = i915_vma_pin(vma, 0, 0, PIN_USER);
 	if (err) {
@@ -89,7 +88,7 @@ create_spin_counter(struct intel_engine_cs *engine,
 
 	base = i915_gem_object_pin_map(obj, I915_MAP_WC);
 	if (IS_ERR(base)) {
-		i915_gem_object_put(obj);
+		i915_vma_put(vma);
 		return ERR_CAST(base);
 	}
 	cs = base;
diff --git a/drivers/gpu/drm/i915/gt/selftest_workarounds.c b/drivers/gpu/drm/i915/gt/selftest_workarounds.c
index febc9e6692ba..ac09bba55a54 100644
--- a/drivers/gpu/drm/i915/gt/selftest_workarounds.c
+++ b/drivers/gpu/drm/i915/gt/selftest_workarounds.c
@@ -128,7 +128,7 @@ read_nonprivs(struct i915_gem_context *ctx, struct intel_engine_cs *engine)
 
 	err = i915_vma_pin(vma, 0, 0, PIN_GLOBAL);
 	if (err)
-		goto err_obj;
+		goto err_vma;
 
 	rq = igt_request_alloc(ctx, engine);
 	if (IS_ERR(rq)) {
@@ -164,6 +164,7 @@ read_nonprivs(struct i915_gem_context *ctx, struct intel_engine_cs *engine)
 
 	i915_request_add(rq);
 	i915_vma_unpin(vma);
+	i915_vma_put(vma);
 
 	return result;
 
@@ -171,6 +172,8 @@ read_nonprivs(struct i915_gem_context *ctx, struct intel_engine_cs *engine)
 	i915_request_add(rq);
 err_pin:
 	i915_vma_unpin(vma);
+err_vma:
+	i915_vma_put(vma);
 err_obj:
 	i915_gem_object_put(result);
 	return ERR_PTR(err);
@@ -370,19 +373,18 @@ static struct i915_vma *create_batch(struct i915_address_space *vm)
 		return ERR_CAST(obj);
 
 	vma = i915_vma_instance(obj, vm, NULL);
-	if (IS_ERR(vma)) {
-		err = PTR_ERR(vma);
-		goto err_obj;
-	}
+	i915_gem_object_put(obj);
+	if (IS_ERR(vma))
+		return vma;
 
 	err = i915_vma_pin(vma, 0, 0, PIN_USER);
 	if (err)
-		goto err_obj;
+		goto err_vma;
 
 	return vma;
 
-err_obj:
-	i915_gem_object_put(obj);
+err_vma:
+	i915_vma_put(vma);
 	return ERR_PTR(err);
 }
 
diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc.c b/drivers/gpu/drm/i915/gt/uc/intel_guc.c
index 446a41946f56..700b236a61ee 100644
--- a/drivers/gpu/drm/i915/gt/uc/intel_guc.c
+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc.c
@@ -673,21 +673,18 @@ struct i915_vma *intel_guc_allocate_vma(struct intel_guc *guc, u32 size)
 		return ERR_CAST(obj);
 
 	vma = i915_vma_instance(obj, &gt->ggtt->vm, NULL);
+	i915_gem_object_put(obj);
 	if (IS_ERR(vma))
-		goto err;
+		return vma;
 
 	flags = PIN_OFFSET_BIAS | i915_ggtt_pin_bias(vma);
 	ret = i915_ggtt_pin(vma, 0, flags);
 	if (ret) {
-		vma = ERR_PTR(ret);
-		goto err;
+		i915_vma_put(vma);
+		return ERR_PTR(ret);
 	}
 
 	return i915_vma_make_unshrinkable(vma);
-
-err:
-	i915_gem_object_put(obj);
-	return vma;
 }
 
 /**
diff --git a/drivers/gpu/drm/i915/gvt/scheduler.c b/drivers/gpu/drm/i915/gvt/scheduler.c
index 1570eb8aa978..d5499b4ef97c 100644
--- a/drivers/gpu/drm/i915/gvt/scheduler.c
+++ b/drivers/gpu/drm/i915/gvt/scheduler.c
@@ -622,8 +622,10 @@ static void release_shadow_batch_buffer(struct intel_vgpu_workload *workload)
 			if (bb->va && !IS_ERR(bb->va))
 				i915_gem_object_unpin_map(bb->obj);
 
-			if (bb->vma && !IS_ERR(bb->vma))
+			if (bb->vma && !IS_ERR(bb->vma)) {
 				i915_vma_unpin(bb->vma);
+				i915_vma_put(bb->vma);
+			}
 
 			i915_gem_object_put(bb->obj);
 		}
diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
index 94ed442910d6..e6c7709a654e 100644
--- a/drivers/gpu/drm/i915/i915_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
@@ -1483,6 +1483,8 @@ gt_drop_caches(struct intel_gt *gt, u64 val)
 		ret = intel_gt_pm_wait_for_idle(gt);
 		if (ret)
 			return ret;
+
+		i915_vma_clock_flush(&gt->vma_clock);
 	}
 
 	if (val & DROP_RESET_ACTIVE && intel_gt_terminally_wedged(gt))
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index ea281d7b0630..fb4610b417e0 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -352,15 +352,21 @@ static int i915_workqueues_init(struct drm_i915_private *dev_priv)
 	 * workqueue at any time.  Use an ordered one.
 	 */
 	dev_priv->wq = alloc_ordered_workqueue("i915", 0);
-	if (dev_priv->wq == NULL)
+	if (!dev_priv->wq)
 		goto out_err;
 
-	dev_priv->hotplug.dp_wq = alloc_ordered_workqueue("i915-dp", 0);
-	if (dev_priv->hotplug.dp_wq == NULL)
+	dev_priv->reclaim_wq = create_workqueue("i915-reclaim");
+	if (!dev_priv->reclaim_wq)
 		goto out_free_wq;
 
+	dev_priv->hotplug.dp_wq = alloc_ordered_workqueue("i915-dp", 0);
+	if (!dev_priv->hotplug.dp_wq)
+		goto out_free_reclaim;
+
 	return 0;
 
+out_free_reclaim:
+	destroy_workqueue(dev_priv->reclaim_wq);
 out_free_wq:
 	destroy_workqueue(dev_priv->wq);
 out_err:
@@ -372,6 +378,7 @@ static int i915_workqueues_init(struct drm_i915_private *dev_priv)
 static void i915_workqueues_cleanup(struct drm_i915_private *dev_priv)
 {
 	destroy_workqueue(dev_priv->hotplug.dp_wq);
+	destroy_workqueue(dev_priv->reclaim_wq);
 	destroy_workqueue(dev_priv->wq);
 }
 
@@ -1078,9 +1085,9 @@ static void i915_driver_release(struct drm_device *dev)
 
 	i915_gem_driver_release(dev_priv);
 
-	intel_memory_regions_driver_release(dev_priv);
 	i915_ggtt_driver_release(dev_priv);
 	i915_gem_drain_freed_objects(dev_priv);
+	intel_memory_regions_driver_release(dev_priv);
 
 	i915_driver_mmio_release(dev_priv);
 
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 0df73a14af1c..b1df307ca4e0 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -966,6 +966,7 @@ struct drm_i915_private {
 	 * result in deadlocks.
 	 */
 	struct workqueue_struct *wq;
+	struct workqueue_struct *reclaim_wq;
 
 	/* ordered wq for modesets */
 	struct workqueue_struct *modeset_wq;
@@ -1744,9 +1745,10 @@ static inline void i915_gem_drain_freed_objects(struct drm_i915_private *i915)
 	 * callbacks could have added new objects into the freed list, and
 	 * armed the work again.
 	 */
+	flush_workqueue(i915->reclaim_wq);
 	while (atomic_read(&i915->mm.free_count)) {
-		flush_work(&i915->mm.free_work);
 		rcu_barrier();
+		flush_work(&i915->mm.free_work);
 	}
 }
 
@@ -1765,8 +1767,6 @@ static inline void i915_gem_drain_workqueue(struct drm_i915_private *i915)
 	 */
 	int pass = 3;
 	do {
-		flush_workqueue(i915->wq);
-		rcu_barrier();
 		i915_gem_drain_freed_objects(i915);
 	} while (--pass);
 	drain_workqueue(i915->wq);
@@ -1782,8 +1782,7 @@ i915_gem_object_ggtt_pin(struct drm_i915_gem_object *obj,
 int i915_gem_object_unbind(struct drm_i915_gem_object *obj,
 			   unsigned long flags);
 #define I915_GEM_OBJECT_UNBIND_ACTIVE BIT(0)
-#define I915_GEM_OBJECT_UNBIND_BARRIER BIT(1)
-#define I915_GEM_OBJECT_UNBIND_TEST BIT(2)
+#define I915_GEM_OBJECT_UNBIND_TEST BIT(1)
 
 void i915_gem_runtime_suspend(struct drm_i915_private *dev_priv);
 
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 0fbe438c4523..2391d146b9ed 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -112,32 +112,16 @@ i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data,
 int i915_gem_object_unbind(struct drm_i915_gem_object *obj,
 			   unsigned long flags)
 {
-	struct intel_runtime_pm *rpm = &to_i915(obj->base.dev)->runtime_pm;
-	LIST_HEAD(still_in_list);
-	intel_wakeref_t wakeref;
-	struct i915_vma *vma;
-	int ret;
+	struct i915_vma *vma, *vn;
+	int ret = 0;
 
 	if (list_empty(&obj->vma.list))
 		return 0;
 
-	/*
-	 * As some machines use ACPI to handle runtime-resume callbacks, and
-	 * ACPI is quite kmalloc happy, we cannot resume beneath the vm->mutex
-	 * as they are required by the shrinker. Ergo, we wake the device up
-	 * first just in case.
-	 */
-	wakeref = intel_runtime_pm_get(rpm);
-
-try_again:
-	ret = 0;
 	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;
+	list_for_each_entry_safe(vma, vn, &obj->vma.list, obj_link) {
+		struct i915_address_space *vm = NULL;
 
-		list_move_tail(&vma->obj_link, &still_in_list);
 		if (!i915_vma_is_bound(vma, I915_VMA_BIND_MASK))
 			continue;
 
@@ -146,36 +130,29 @@ int i915_gem_object_unbind(struct drm_i915_gem_object *obj,
 			break;
 		}
 
-		ret = -EAGAIN;
-		if (!i915_vm_tryopen(vm))
-			break;
-
-		/* Prevent vma being freed by i915_vma_parked as we unbind */
-		vma = __i915_vma_get(vma);
+		if (!i915_vma_tryget(vma))
+			vm = i915_vm_get(vma->vm);
 		spin_unlock(&obj->vma.lock);
 
-		if (vma) {
+		if (vm) {
+			flush_work(&vm->release.work);
+			i915_vm_put(vm);
+		} else {
 			ret = -EBUSY;
 			if (flags & I915_GEM_OBJECT_UNBIND_ACTIVE ||
 			    !i915_vma_is_active(vma))
 				ret = i915_vma_unbind(vma);
-
-			__i915_vma_put(vma);
+			i915_vma_put(vma);
+			if (ret)
+				return ret;
 		}
 
-		i915_vm_close(vm);
+		/* Reset iteration after dropping lock */
 		spin_lock(&obj->vma.lock);
+		vn = list_first_entry(&obj->vma.list, typeof(*vn), obj_link);
 	}
-	list_splice_init(&still_in_list, &obj->vma.list);
 	spin_unlock(&obj->vma.lock);
 
-	if (ret == -EAGAIN && flags & I915_GEM_OBJECT_UNBIND_BARRIER) {
-		rcu_barrier(); /* flush the i915_vm_release() */
-		goto try_again;
-	}
-
-	intel_runtime_pm_put(rpm, wakeref);
-
 	return ret;
 }
 
@@ -403,26 +380,14 @@ i915_gem_gtt_pread(struct drm_i915_gem_object *obj,
 	struct drm_mm_node node;
 	struct dma_fence *fence;
 	void __user *user_data;
-	struct i915_vma *vma;
 	u64 remain, offset;
 	int ret;
 
 	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
-	vma = ERR_PTR(-ENODEV);
-	if (!i915_gem_object_is_tiled(obj))
-		vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0,
-					       PIN_MAPPABLE |
-					       PIN_NONBLOCK /* NOWARN */ |
-					       PIN_NOEVICT);
-	if (!IS_ERR(vma)) {
-		node.start = i915_ggtt_offset(vma);
-		node.flags = 0;
-	} else {
-		ret = insert_mappable_node(ggtt, &node, PAGE_SIZE);
-		if (ret)
-			goto out_rpm;
-		GEM_BUG_ON(!drm_mm_node_allocated(&node));
-	}
+	ret = insert_mappable_node(ggtt, &node, PAGE_SIZE);
+	if (ret)
+		goto out_rpm;
+	GEM_BUG_ON(!drm_mm_node_allocated(&node));
 
 	ret = i915_gem_object_lock_interruptible(obj);
 	if (ret)
@@ -477,12 +442,8 @@ i915_gem_gtt_pread(struct drm_i915_gem_object *obj,
 
 	i915_gem_object_unlock_fence(obj, fence);
 out_unpin:
-	if (drm_mm_node_allocated(&node)) {
-		ggtt->vm.clear_range(&ggtt->vm, node.start, node.size);
-		remove_mappable_node(ggtt, &node);
-	} else {
-		i915_vma_unpin(vma);
-	}
+	ggtt->vm.clear_range(&ggtt->vm, node.start, node.size);
+	remove_mappable_node(ggtt, &node);
 out_rpm:
 	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
 	return ret;
@@ -586,9 +547,8 @@ i915_gem_gtt_pwrite_fast(struct drm_i915_gem_object *obj,
 	intel_wakeref_t wakeref;
 	struct drm_mm_node node;
 	struct dma_fence *fence;
-	struct i915_vma *vma;
-	u64 remain, offset;
 	void __user *user_data;
+	u64 remain, offset;
 	int ret;
 
 	if (i915_gem_object_has_struct_page(obj)) {
@@ -607,21 +567,10 @@ i915_gem_gtt_pwrite_fast(struct drm_i915_gem_object *obj,
 		wakeref = intel_runtime_pm_get(rpm);
 	}
 
-	vma = ERR_PTR(-ENODEV);
-	if (!i915_gem_object_is_tiled(obj))
-		vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0,
-					       PIN_MAPPABLE |
-					       PIN_NONBLOCK /* NOWARN */ |
-					       PIN_NOEVICT);
-	if (!IS_ERR(vma)) {
-		node.start = i915_ggtt_offset(vma);
-		node.flags = 0;
-	} else {
-		ret = insert_mappable_node(ggtt, &node, PAGE_SIZE);
-		if (ret)
-			goto out_rpm;
-		GEM_BUG_ON(!drm_mm_node_allocated(&node));
-	}
+	ret = insert_mappable_node(ggtt, &node, PAGE_SIZE);
+	if (ret)
+		goto out_rpm;
+	GEM_BUG_ON(!drm_mm_node_allocated(&node));
 
 	ret = i915_gem_object_lock_interruptible(obj);
 	if (ret)
@@ -688,12 +637,8 @@ i915_gem_gtt_pwrite_fast(struct drm_i915_gem_object *obj,
 
 	i915_gem_object_unlock_fence(obj, fence);
 out_unpin:
-	if (drm_mm_node_allocated(&node)) {
-		ggtt->vm.clear_range(&ggtt->vm, node.start, node.size);
-		remove_mappable_node(ggtt, &node);
-	} else {
-		i915_vma_unpin(vma);
-	}
+	ggtt->vm.clear_range(&ggtt->vm, node.start, node.size);
+	remove_mappable_node(ggtt, &node);
 out_rpm:
 	intel_runtime_pm_put(rpm, wakeref);
 	return ret;
@@ -1005,35 +950,45 @@ i915_gem_object_ggtt_pin(struct drm_i915_gem_object *obj,
 		return vma;
 
 	if (i915_vma_misplaced(vma, size, alignment, flags)) {
-		if (flags & PIN_NOEVICT)
-			return ERR_PTR(-ENOSPC);
+		if (flags & PIN_NOEVICT) {
+			ret = -ENOSPC;
+			goto err_put;
+		}
 
 		if (flags & PIN_NONBLOCK) {
-			if (i915_vma_is_pinned(vma) || i915_vma_is_active(vma))
-				return ERR_PTR(-ENOSPC);
+			if (i915_vma_is_pinned(vma) ||
+			    i915_vma_is_active(vma)) {
+				ret = -ENOSPC;
+				goto err_put;
+			}
 
 			if (flags & PIN_MAPPABLE &&
-			    vma->fence_size > ggtt->mappable_end / 2)
-				return ERR_PTR(-ENOSPC);
+			    vma->fence_size > ggtt->mappable_end / 2) {
+				ret = -ENOSPC;
+				goto err_put;
+			}
 		}
 
 		if (i915_vma_is_pinned(vma) || i915_vma_is_active(vma)) {
 			discard_ggtt_vma(vma);
+			i915_vma_put(vma);
 			goto new_vma;
 		}
 
 		ret = i915_vma_unbind(vma);
 		if (ret)
-			return ERR_PTR(ret);
+			goto err_put;
 	}
 
 	if (flags & PIN_NONBLOCK &&
-	    i915_active_fence_isset(&vma->active.excl))
-		return ERR_PTR(-EAGAIN);
+	    i915_active_fence_isset(&vma->active.excl)) {
+		ret = -EAGAIN;
+		goto err_put;
+	}
 
 	ret = i915_vma_pin(vma, size, alignment, flags | PIN_GLOBAL);
 	if (ret)
-		return ERR_PTR(ret);
+		goto err_put;
 
 	if (vma->fence && !i915_gem_object_is_tiled(obj)) {
 		mutex_lock(&ggtt->vm.mutex);
@@ -1042,12 +997,16 @@ i915_gem_object_ggtt_pin(struct drm_i915_gem_object *obj,
 	}
 
 	ret = i915_vma_wait_for_bind(vma);
-	if (ret) {
-		i915_vma_unpin(vma);
-		return ERR_PTR(ret);
-	}
+	if (ret)
+		goto err_unpin;
 
 	return vma;
+
+err_unpin:
+	i915_vma_unpin(vma);
+err_put:
+	i915_vma_put(vma);
+	return ERR_PTR(ret);
 }
 
 int
@@ -1214,6 +1173,7 @@ void i915_gem_driver_unregister(struct drm_i915_private *i915)
 void i915_gem_driver_remove(struct drm_i915_private *dev_priv)
 {
 	intel_wakeref_auto_fini(&dev_priv->ggtt.userfault_wakeref);
+	flush_work(&dev_priv->ggtt.vm.release.work);
 
 	i915_gem_suspend_late(dev_priv);
 	intel_gt_driver_remove(&dev_priv->gt);
diff --git a/drivers/gpu/drm/i915/i915_perf.c b/drivers/gpu/drm/i915/i915_perf.c
index 25329b7600c9..e6fcd290b067 100644
--- a/drivers/gpu/drm/i915/i915_perf.c
+++ b/drivers/gpu/drm/i915/i915_perf.c
@@ -1325,9 +1325,7 @@ static void oa_put_render_ctx_id(struct i915_perf_stream *stream)
 static void
 free_oa_buffer(struct i915_perf_stream *stream)
 {
-	i915_vma_unpin_and_release(&stream->oa_buffer.vma,
-				   I915_VMA_RELEASE_MAP);
-
+	__i915_ggtt_unpin(&stream->oa_buffer.vma, I915_VMA_RELEASE_MAP);
 	stream->oa_buffer.vaddr = NULL;
 }
 
@@ -1344,7 +1342,7 @@ free_oa_configs(struct i915_perf_stream *stream)
 static void
 free_noa_wait(struct i915_perf_stream *stream)
 {
-	i915_vma_unpin_and_release(&stream->noa_wait, 0);
+	__i915_ggtt_unpin(&stream->noa_wait, 0);
 }
 
 static void i915_oa_stream_destroy(struct i915_perf_stream *stream)
@@ -1548,23 +1546,21 @@ static int alloc_oa_buffer(struct i915_perf_stream *stream)
 	BUILD_BUG_ON(OA_BUFFER_SIZE < SZ_128K || OA_BUFFER_SIZE > SZ_16M);
 
 	bo = i915_gem_object_create_shmem(stream->perf->i915, OA_BUFFER_SIZE);
-	if (IS_ERR(bo)) {
-		drm_err(&i915->drm, "Failed to allocate OA buffer\n");
+	if (IS_ERR(bo))
 		return PTR_ERR(bo);
-	}
 
 	i915_gem_object_set_cache_coherency(bo, I915_CACHE_LLC);
 
 	/* PreHSW required 512K alignment, HSW requires 16M */
 	vma = i915_gem_object_ggtt_pin(bo, NULL, 0, SZ_16M, 0);
+	i915_gem_object_put(bo);
 	if (IS_ERR(vma)) {
 		ret = PTR_ERR(vma);
-		goto err_unref;
+		goto err;
 	}
 	stream->oa_buffer.vma = vma;
 
-	stream->oa_buffer.vaddr =
-		i915_gem_object_pin_map(bo, I915_MAP_WB);
+	stream->oa_buffer.vaddr = i915_gem_object_pin_map(bo, I915_MAP_WB);
 	if (IS_ERR(stream->oa_buffer.vaddr)) {
 		ret = PTR_ERR(stream->oa_buffer.vaddr);
 		goto err_unpin;
@@ -1573,14 +1569,9 @@ static int alloc_oa_buffer(struct i915_perf_stream *stream)
 	return 0;
 
 err_unpin:
-	__i915_vma_unpin(vma);
-
-err_unref:
-	i915_gem_object_put(bo);
-
+	__i915_ggtt_unpin(&stream->oa_buffer.vma, 0);
+err:
 	stream->oa_buffer.vaddr = NULL;
-	stream->oa_buffer.vma = NULL;
-
 	return ret;
 }
 
@@ -1639,10 +1630,9 @@ static int alloc_noa_wait(struct i915_perf_stream *stream)
 	 * needs to be fixed during the lifetime of the i915/perf stream.
 	 */
 	vma = i915_gem_object_ggtt_pin(bo, NULL, 0, 0, PIN_HIGH);
-	if (IS_ERR(vma)) {
-		ret = PTR_ERR(vma);
-		goto err_unref;
-	}
+	i915_gem_object_put(bo);
+	if (IS_ERR(vma))
+		return PTR_ERR(vma);
 
 	batch = cs = i915_gem_object_pin_map(bo, I915_MAP_WB);
 	if (IS_ERR(batch)) {
@@ -1778,9 +1768,7 @@ static int alloc_noa_wait(struct i915_perf_stream *stream)
 	return 0;
 
 err_unpin:
-	i915_vma_unpin_and_release(&vma, 0);
-err_unref:
-	i915_gem_object_put(bo);
+	i915_ggtt_unpin(vma);
 	return ret;
 }
 
@@ -1879,6 +1867,7 @@ alloc_oa_config_buffer(struct i915_perf_stream *stream,
 
 	oa_bo->oa_config = i915_oa_config_get(oa_config);
 	llist_add(&oa_bo->node, &stream->oa_config_bos);
+	i915_gem_object_put(obj);
 
 	return oa_bo;
 
diff --git a/drivers/gpu/drm/i915/i915_vma.c b/drivers/gpu/drm/i915/i915_vma.c
index 5623f6e8373d..c8e9920bd6cb 100644
--- a/drivers/gpu/drm/i915/i915_vma.c
+++ b/drivers/gpu/drm/i915/i915_vma.c
@@ -90,7 +90,8 @@ static inline struct i915_vma *active_to_vma(struct i915_active *ref)
 
 static int __i915_vma_active(struct i915_active *ref)
 {
-	return i915_vma_tryget(active_to_vma(ref)) ? 0 : -ENOENT;
+	i915_vma_get(active_to_vma(ref));
+	return 0;
 }
 
 __i915_active_call
@@ -118,7 +119,7 @@ vma_create(struct drm_i915_gem_object *obj,
 	kref_init(&vma->ref);
 	vma->vm = i915_vm_get(vm);
 	vma->ops = &vm->vma_ops;
-	vma->obj = obj;
+	vma->obj = i915_gem_object_get(obj);
 	vma->resv = obj->base.resv;
 	vma->size = obj->base.size;
 	vma->display_alignment = I915_GTT_MIN_ALIGNMENT;
@@ -200,8 +201,10 @@ vma_create(struct drm_i915_gem_object *obj,
 			p = &rb->rb_right;
 		else if (cmp > 0)
 			p = &rb->rb_left;
-		else
+		else if (i915_vma_tryget(pos))
 			goto err_unlock;
+		else
+			p = &rb->rb_left;
 	}
 	rb_link_node(&vma->obj_node, rb, p);
 	rb_insert_color(&vma->obj_node, &obj->vma.tree);
@@ -224,6 +227,7 @@ vma_create(struct drm_i915_gem_object *obj,
 err_unlock:
 	spin_unlock(&obj->vma.lock);
 err_vma:
+	i915_gem_object_put(obj);
 	i915_vm_put(vm);
 	i915_vma_free(vma);
 	return pos;
@@ -262,8 +266,6 @@ vma_lookup(struct drm_i915_gem_object *obj,
  *
  * i915_vma_instance() looks up an existing VMA of the @obj in the @vm with
  * the same @view characteristics. If a match is not found, one is created.
- * Once created, the VMA is kept until either the object is freed, or the
- * address space is closed.
  *
  * Returns the vma, or an error pointer.
  */
@@ -279,6 +281,8 @@ i915_vma_instance(struct drm_i915_gem_object *obj,
 
 	spin_lock(&obj->vma.lock);
 	vma = vma_lookup(obj, vm, view);
+	if (vma)
+		vma = i915_vma_tryget(vma);
 	spin_unlock(&obj->vma.lock);
 
 	/* vma_create() will resolve the race if another creates the vma */
@@ -532,21 +536,20 @@ void i915_vma_unpin_iomap(struct i915_vma *vma)
 void i915_vma_unpin_and_release(struct i915_vma **p_vma, unsigned int flags)
 {
 	struct i915_vma *vma;
-	struct drm_i915_gem_object *obj;
 
 	vma = fetch_and_zero(p_vma);
 	if (!vma)
 		return;
 
-	obj = vma->obj;
-	GEM_BUG_ON(!obj);
+	if (flags & I915_VMA_RELEASE_FENCE)
+		i915_vma_unpin_fence(vma);
 
 	i915_vma_unpin(vma);
 
 	if (flags & I915_VMA_RELEASE_MAP)
-		i915_gem_object_unpin_map(obj);
+		i915_gem_object_unpin_map(vma->obj);
 
-	i915_gem_object_put(obj);
+	i915_vma_put(vma);
 }
 
 bool i915_vma_misplaced(const struct i915_vma *vma,
@@ -891,11 +894,6 @@ int i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
 
 	/* No more allocations allowed now we hold vm->mutex */
 
-	if (unlikely(i915_vma_is_closed(vma))) {
-		err = -ENOENT;
-		goto err_unlock;
-	}
-
 	bound = atomic_read(&vma->flags);
 	if (unlikely(bound & I915_VMA_ERROR)) {
 		err = -ENOMEM;
@@ -1000,8 +998,34 @@ int i915_ggtt_pin(struct i915_vma *vma, u32 align, unsigned int flags)
 	} while (1);
 }
 
-static void __vma_close(struct i915_vma *vma, struct intel_gt *gt)
+void i915_vma_open(struct i915_vma *vma)
+{
+	i915_vma_get(vma);
+
+	if (!atomic_add_unless(&vma->open_count, 1, 0)) {
+		struct i915_vma_clock *clock = &vma->vm->gt->vma_clock;
+
+		spin_lock(&clock->lock);
+		if (!atomic_add_unless(&vma->open_count, 1, 0)) {
+			if (!list_empty(&vma->closed_link)) {
+				list_del_init(&vma->closed_link);
+				i915_vma_put(vma);
+			}
+			atomic_set_release(&vma->open_count, 1);
+		}
+		spin_unlock(&clock->lock);
+	}
+}
+
+static bool object_inuse(struct drm_i915_gem_object *obj)
+{
+	return READ_ONCE(obj->base.handle_count);
+}
+
+void i915_vma_close(struct i915_vma *vma)
 {
+	struct i915_vma_clock *clock = &vma->vm->gt->vma_clock;
+
 	/*
 	 * We defer actually closing, unbinding and destroying the VMA until
 	 * the next idle point, or if the object is freed in the meantime. By
@@ -1014,108 +1038,103 @@ static void __vma_close(struct i915_vma *vma, struct intel_gt *gt)
 	 * causing us to rebind the VMA once more. This ends up being a lot
 	 * of wasted work for the steady state.
 	 */
-	GEM_BUG_ON(i915_vma_is_closed(vma));
-	list_add(&vma->closed_link, &gt->closed_vma);
-}
-
-void i915_vma_close(struct i915_vma *vma)
-{
-	struct intel_gt *gt = vma->vm->gt;
-	unsigned long flags;
-
-	if (i915_vma_is_ggtt(vma))
-		return;
 
 	GEM_BUG_ON(!atomic_read(&vma->open_count));
-	if (atomic_dec_and_lock_irqsave(&vma->open_count,
-					&gt->closed_lock,
-					flags)) {
-		__vma_close(vma, gt);
-		spin_unlock_irqrestore(&gt->closed_lock, flags);
-	}
-}
+	if (atomic_dec_and_lock(&vma->open_count, &clock->lock)) {
+		GEM_BUG_ON(i915_vma_is_closed(vma));
+		if (object_inuse(vma->obj)) {
+			i915_vma_get(vma);
+			list_add(&vma->closed_link, &clock->age[0]);
+		}
+		spin_unlock(&clock->lock);
 
-static void __i915_vma_remove_closed(struct i915_vma *vma)
-{
-	struct intel_gt *gt = vma->vm->gt;
+		if (!list_empty(&vma->closed_link))
+			schedule_delayed_work(&clock->work,
+					      round_jiffies_up_relative(HZ));
+	}
 
-	spin_lock_irq(&gt->closed_lock);
-	list_del_init(&vma->closed_link);
-	spin_unlock_irq(&gt->closed_lock);
+	i915_vma_put(vma);
 }
 
-void i915_vma_reopen(struct i915_vma *vma)
+static void __release_unbind(struct i915_vma *vma)
 {
-	if (i915_vma_is_closed(vma))
-		__i915_vma_remove_closed(vma);
+	struct i915_address_space *vm = vma->vm;
+	unsigned long flags;
+	bool first;
+
+	spin_lock_irqsave(&vm->release.lock, flags);
+	list_add_tail(&vma->closed_link, &vm->release.list);
+	first = list_is_first(&vma->closed_link, &vm->release.list);
+	spin_unlock_irqrestore(&vm->release.lock, flags);
+	if (first)
+		queue_work(vm->i915->reclaim_wq, &vm->release.work);
 }
 
 void i915_vma_release(struct kref *ref)
 {
 	struct i915_vma *vma = container_of(ref, typeof(*vma), ref);
 
-	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));
+	if (drm_mm_node_allocated(&vma->node) || vma->obj) {
+		GEM_BUG_ON(i915_vma_is_closed(vma));
+		__release_unbind(vma);
+		return;
 	}
 	GEM_BUG_ON(i915_vma_is_active(vma));
 	GEM_BUG_ON(!list_empty(&vma->vm_link));
 
-	if (vma->obj) {
-		struct drm_i915_gem_object *obj = vma->obj;
-
-		spin_lock(&obj->vma.lock);
-		list_del(&vma->obj_link);
-		if (!RB_EMPTY_NODE(&vma->obj_node))
-			rb_erase(&vma->obj_node, &obj->vma.tree);
-		spin_unlock(&obj->vma.lock);
-	}
-
-	__i915_vma_remove_closed(vma);
 	i915_vm_put(vma->vm);
 
 	i915_active_fini(&vma->active);
 	i915_vma_free(vma);
 }
 
-void i915_vma_parked(struct intel_gt *gt)
+static void i915_vma_clock(struct work_struct *w)
 {
+	struct i915_vma_clock *clock =
+		container_of(w, typeof(*clock), work.work);
 	struct i915_vma *vma, *next;
 	LIST_HEAD(closed);
 
-	spin_lock_irq(&gt->closed_lock);
-	list_for_each_entry_safe(vma, next, &gt->closed_vma, closed_link) {
-		struct drm_i915_gem_object *obj = vma->obj;
-		struct i915_address_space *vm = vma->vm;
+	/*
+	 * A very simple clock aging algorithm: we keep the user's closed
+	 * vma alive for a couple of timer ticks before destroying them.
+	 * This serves a shortlived cache so that frequently reused VMA
+	 * are kept alive between frames and we skip having to rebing them.
+	 *
+	 * When closed, we insert the vma into age[0]. Upon completion of
+	 * a timer tick, it is moved to age[1]. At the start of each timer
+	 * tick, we destroy all the old vma that were accumulated into age[1]
+	 * and have not been reused. All destroyed vma have therefore been
+	 * unused for more than 1 tick (at least a second), and at most 2
+	 * ticks (we expect the average to be 1.5 ticks).
+	 */
 
-		/* XXX All to avoid keeping a reference on i915_vma itself */
+	spin_lock(&clock->lock);
 
-		if (!kref_get_unless_zero(&obj->base.refcount))
-			continue;
+	list_for_each_entry_safe(vma, next, &clock->age[1], closed_link) {
+		list_del_init(&vma->closed_link);
 
-		if (!i915_vm_tryopen(vm)) {
-			i915_gem_object_put(obj);
+		if (i915_vma_is_active(vma) && object_inuse(vma->obj)) {
+			list_add_tail(&vma->closed_link, &clock->age[0]);
 			continue;
 		}
 
-		list_move(&vma->closed_link, &closed);
-	}
-	spin_unlock_irq(&gt->closed_lock);
-
-	/* As the GT is held idle, no vma can be reopened as we destroy them */
-	list_for_each_entry_safe(vma, next, &closed, closed_link) {
-		struct drm_i915_gem_object *obj = vma->obj;
-		struct i915_address_space *vm = vma->vm;
+		spin_unlock(&clock->lock);
 
-		INIT_LIST_HEAD(&vma->closed_link);
-		__i915_vma_put(vma);
+		i915_vma_put(vma);
 
-		i915_gem_object_put(obj);
-		i915_vm_close(vm);
+		/* Restart after dropping lock */
+		spin_lock(&clock->lock);
+		next = list_first_entry(&clock->age[1],
+					typeof(*next), closed_link);
 	}
+	list_splice_tail_init(&clock->age[0], &clock->age[1]);
+
+	if (!list_empty(&clock->age[1]))
+		schedule_delayed_work(&clock->work,
+				      round_jiffies_up_relative(HZ));
+
+	spin_unlock(&clock->lock);
 }
 
 static void __i915_vma_iounmap(struct i915_vma *vma)
@@ -1134,7 +1153,7 @@ void i915_vma_revoke_mmap(struct i915_vma *vma)
 	struct drm_vma_offset_node *node;
 	u64 vma_offset;
 
-	if (!i915_vma_has_userfault(vma))
+	if (!i915_vma_unset_userfault(vma))
 		return;
 
 	GEM_BUG_ON(!i915_vma_is_map_and_fenceable(vma));
@@ -1147,9 +1166,10 @@ void i915_vma_revoke_mmap(struct i915_vma *vma)
 			    vma->size,
 			    1);
 
-	i915_vma_unset_userfault(vma);
 	if (!--vma->obj->userfault_count)
 		list_del(&vma->obj->userfault_link);
+
+	i915_vma_close(vma);
 }
 
 int __i915_vma_move_to_active(struct i915_vma *vma, struct i915_request *rq)
@@ -1347,6 +1367,23 @@ void i915_vma_make_purgeable(struct i915_vma *vma)
 	i915_gem_object_make_purgeable(vma->obj);
 }
 
+void i915_vma_clock_init_early(struct i915_vma_clock *clock)
+{
+	spin_lock_init(&clock->lock);
+	INIT_LIST_HEAD(&clock->age[0]);
+	INIT_LIST_HEAD(&clock->age[1]);
+
+	INIT_DELAYED_WORK(&clock->work, i915_vma_clock);
+}
+
+void i915_vma_clock_flush(struct i915_vma_clock *clock)
+{
+	do {
+		if (cancel_delayed_work_sync(&clock->work))
+			i915_vma_clock(&clock->work.work);
+	} while (delayed_work_pending(&clock->work));
+}
+
 #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
 #include "selftests/i915_vma.c"
 #endif
diff --git a/drivers/gpu/drm/i915/i915_vma.h b/drivers/gpu/drm/i915/i915_vma.h
index 9a26e6cbe8cd..cb55610d396c 100644
--- a/drivers/gpu/drm/i915/i915_vma.h
+++ b/drivers/gpu/drm/i915/i915_vma.h
@@ -45,7 +45,18 @@ i915_vma_instance(struct drm_i915_gem_object *obj,
 		  const struct i915_ggtt_view *view);
 
 void i915_vma_unpin_and_release(struct i915_vma **p_vma, unsigned int flags);
-#define I915_VMA_RELEASE_MAP BIT(0)
+#define I915_VMA_RELEASE_FENCE BIT(0)
+#define I915_VMA_RELEASE_MAP   BIT(1)
+
+static inline void __i915_ggtt_unpin(struct i915_vma **vma, unsigned int flags)
+{
+	i915_vma_unpin_and_release(vma, flags);
+}
+
+static inline void i915_ggtt_unpin(struct i915_vma *vma)
+{
+	__i915_ggtt_unpin(&vma, 0);
+}
 
 static inline bool i915_vma_is_active(const struct i915_vma *vma)
 {
@@ -95,9 +106,10 @@ static inline bool i915_vma_set_userfault(struct i915_vma *vma)
 	return test_and_set_bit(I915_VMA_USERFAULT_BIT, __i915_vma_flags(vma));
 }
 
-static inline void i915_vma_unset_userfault(struct i915_vma *vma)
+static inline bool i915_vma_unset_userfault(struct i915_vma *vma)
 {
-	return clear_bit(I915_VMA_USERFAULT_BIT, __i915_vma_flags(vma));
+	return test_and_clear_bit(I915_VMA_USERFAULT_BIT,
+				  __i915_vma_flags(vma));
 }
 
 static inline bool i915_vma_has_userfault(const struct i915_vma *vma)
@@ -124,15 +136,17 @@ static inline u32 i915_ggtt_pin_bias(struct i915_vma *vma)
 	return i915_vm_to_ggtt(vma->vm)->pin_bias;
 }
 
+void i915_vma_release(struct kref *ref);
+
 static inline struct i915_vma *i915_vma_get(struct i915_vma *vma)
 {
-	i915_gem_object_get(vma->obj);
+	kref_get(&vma->ref);
 	return vma;
 }
 
 static inline struct i915_vma *i915_vma_tryget(struct i915_vma *vma)
 {
-	if (likely(kref_get_unless_zero(&vma->obj->base.refcount)))
+	if (kref_get_unless_zero(&vma->ref))
 		return vma;
 
 	return NULL;
@@ -140,7 +154,7 @@ static inline struct i915_vma *i915_vma_tryget(struct i915_vma *vma)
 
 static inline void i915_vma_put(struct i915_vma *vma)
 {
-	i915_gem_object_put(vma->obj);
+	kref_put(&vma->ref, i915_vma_release);
 }
 
 static __always_inline ptrdiff_t ptrdiff(const void *a, const void *b)
@@ -207,22 +221,8 @@ void __i915_vma_evict(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_open(struct i915_vma *vma);
 void i915_vma_close(struct i915_vma *vma);
-void i915_vma_reopen(struct i915_vma *vma);
-
-static inline struct i915_vma *__i915_vma_get(struct i915_vma *vma)
-{
-	if (kref_get_unless_zero(&vma->ref))
-		return vma;
-
-	return NULL;
-}
-
-void i915_vma_release(struct kref *ref);
-static inline void __i915_vma_put(struct i915_vma *vma)
-{
-	kref_put(&vma->ref, i915_vma_release);
-}
 
 #define assert_vma_held(vma) dma_resv_assert_held((vma)->resv)
 
@@ -357,8 +357,6 @@ i915_vma_unpin_fence(struct i915_vma *vma)
 		__i915_vma_unpin_fence(vma);
 }
 
-void i915_vma_parked(struct intel_gt *gt);
-
 #define for_each_until(cond) if (cond) break; else
 
 /**
@@ -390,4 +388,13 @@ static inline int i915_vma_sync(struct i915_vma *vma)
 	return i915_active_wait(&vma->active);
 }
 
+struct i915_vma_clock {
+	spinlock_t lock;
+	struct list_head age[2];
+	struct delayed_work work;
+};
+
+void i915_vma_clock_init_early(struct i915_vma_clock *clock);
+void i915_vma_clock_flush(struct i915_vma_clock *clock);
+
 #endif
diff --git a/drivers/gpu/drm/i915/i915_vma_types.h b/drivers/gpu/drm/i915/i915_vma_types.h
index 02c1640bb034..d7c874a89c47 100644
--- a/drivers/gpu/drm/i915/i915_vma_types.h
+++ b/drivers/gpu/drm/i915/i915_vma_types.h
@@ -271,6 +271,8 @@ struct i915_vma {
 	struct list_head evict_link;
 
 	struct list_head closed_link;
+
+	I915_SELFTEST_DECLARE(struct list_head st_link;)
 };
 
 #endif
diff --git a/drivers/gpu/drm/i915/intel_memory_region.c b/drivers/gpu/drm/i915/intel_memory_region.c
index 6b5e9d88646d..1c7388ca7ad4 100644
--- a/drivers/gpu/drm/i915/intel_memory_region.c
+++ b/drivers/gpu/drm/i915/intel_memory_region.c
@@ -289,6 +289,16 @@ void intel_memory_regions_driver_release(struct drm_i915_private *i915)
 {
 	int i;
 
+	/*
+	 * Make sure that all the delayed objects frees are before we reap the
+	 * memory regions. This is primarily for the SYSTEM_STOLEN region
+	 * which is not exclusively used by the objects.
+	 *
+	 * XXX we need to tighten the flow of object release during shutdown
+	 */
+	rcu_barrier();
+	i915_gem_drain_freed_objects(i915);
+
 	for (i = 0; i < ARRAY_SIZE(i915->mm.regions); i++) {
 		struct intel_memory_region *region =
 			fetch_and_zero(&i915->mm.regions[i]);
diff --git a/drivers/gpu/drm/i915/selftests/i915_gem_evict.c b/drivers/gpu/drm/i915/selftests/i915_gem_evict.c
index 028baae9631f..0d4ddf55533e 100644
--- a/drivers/gpu/drm/i915/selftests/i915_gem_evict.c
+++ b/drivers/gpu/drm/i915/selftests/i915_gem_evict.c
@@ -82,26 +82,44 @@ static int populate_ggtt(struct i915_ggtt *ggtt, struct list_head *objects)
 
 static void unpin_ggtt(struct i915_ggtt *ggtt)
 {
-	struct i915_vma *vma;
+	struct i915_vma *vma, *vn;
+
+	list_for_each_entry_safe(vma, vn, &ggtt->vm.bound_list, vm_link) {
+		if (!vma->obj->mm.quirked)
+			continue;
 
-	list_for_each_entry(vma, &ggtt->vm.bound_list, vm_link)
-		if (vma->obj->mm.quirked)
-			i915_vma_unpin(vma);
+		vma->obj->mm.quirked = false;
+		i915_ggtt_unpin(vma);
+	}
 }
 
 static void cleanup_objects(struct i915_ggtt *ggtt, struct list_head *list)
 {
 	struct drm_i915_gem_object *obj, *on;
 
-	list_for_each_entry_safe(obj, on, list, st_link) {
-		GEM_BUG_ON(!obj->mm.quirked);
-		obj->mm.quirked = false;
+	unpin_ggtt(ggtt);
+	list_for_each_entry_safe(obj, on, list, st_link)
 		i915_gem_object_put(obj);
-	}
 
 	i915_gem_drain_freed_objects(ggtt->vm.i915);
 }
 
+static int probe_full(struct i915_address_space *vm)
+{
+	struct drm_mm_node node = {};
+	int err;
+
+	err = drm_mm_insert_node_in_range(&vm->mm, &node,
+					  I915_GTT_PAGE_SIZE, 0,
+					  I915_COLOR_UNEVICTABLE,
+					  0, U64_MAX,
+					  DRM_MM_INSERT_BEST);
+	if (err == 0)
+		drm_mm_remove_node(&node);
+
+	return err;
+}
+
 static int igt_evict_something(void *arg)
 {
 	struct intel_gt *gt = arg;
@@ -117,13 +135,15 @@ static int igt_evict_something(void *arg)
 
 	/* 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);
+	err = probe_full(&ggtt->vm);
+	if (err == -ENOSPC)
+		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",
+		pr_err("i915_gem_evict_something failed on a pinned full GGTT with err=%d\n",
 		       err);
 		goto cleanup;
 	}
@@ -132,13 +152,15 @@ static int igt_evict_something(void *arg)
 
 	/* 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);
+	err = probe_full(&ggtt->vm);
+	if (err == -ENOSPC)
+		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",
+		pr_err("i915_gem_evict_something failed on a [unpinned] full GGTT with err=%d\n",
 		       err);
 		goto cleanup;
 	}
@@ -293,7 +315,7 @@ static int igt_evict_for_cache_color(void *arg)
 		goto cleanup;
 	}
 
-	i915_vma_unpin(vma);
+	i915_ggtt_unpin(vma);
 
 	/* Remove just the second vma */
 	mutex_lock(&ggtt->vm.mutex);
diff --git a/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c b/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
index e5e6973eb6ea..b31e8b2ade2e 100644
--- a/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
+++ b/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
@@ -141,6 +141,21 @@ fake_dma_object(struct drm_i915_private *i915, u64 size)
 	return ERR_PTR(-ENOMEM);
 }
 
+static struct i915_vma *fake_vma(struct i915_address_space *vm, u64 size)
+{
+	struct drm_i915_gem_object *obj;
+	struct i915_vma *vma;
+
+	obj = fake_dma_object(vm->i915, size);
+	if (IS_ERR(obj))
+		return ERR_CAST(obj);
+
+	vma = i915_vma_instance(obj, vm, NULL);
+	i915_gem_object_put(obj);
+
+	return vma;
+}
+
 static int igt_ppgtt_alloc(void *arg)
 {
 	struct drm_i915_private *dev_priv = arg;
@@ -344,22 +359,13 @@ static int lowlevel_hole(struct i915_address_space *vm,
 	return 0;
 }
 
-static void close_object_list(struct list_head *objects,
-			      struct i915_address_space *vm)
+static void close_vma_list(struct list_head *list)
 {
-	struct drm_i915_gem_object *obj, *on;
-	int ignored;
+	struct i915_vma *vma, *vn;
 
-	list_for_each_entry_safe(obj, on, objects, st_link) {
-		struct i915_vma *vma;
-
-		vma = i915_vma_instance(obj, vm, NULL);
-		if (!IS_ERR(vma))
-			ignored = i915_vma_unbind(vma);
-
-		list_del(&obj->st_link);
-		i915_gem_object_put(obj);
-	}
+	list_for_each_entry_safe(vma, vn, list, st_link)
+		i915_vma_put(vma);
+	INIT_LIST_HEAD(list);
 }
 
 static int fill_hole(struct i915_address_space *vm,
@@ -367,13 +373,12 @@ static int fill_hole(struct i915_address_space *vm,
 		     unsigned long end_time)
 {
 	const u64 hole_size = hole_end - hole_start;
-	struct drm_i915_gem_object *obj;
 	const unsigned long max_pages =
 		min_t(u64, ULONG_MAX - 1, hole_size/2 >> PAGE_SHIFT);
 	const unsigned long max_step = max(int_sqrt(max_pages), 2UL);
 	unsigned long npages, prime, flags;
 	struct i915_vma *vma;
-	LIST_HEAD(objects);
+	LIST_HEAD(vmas);
 	int err;
 
 	/* Try binding many VMA working inwards from either edge */
@@ -395,11 +400,11 @@ static int fill_hole(struct i915_address_space *vm,
 				{ }
 			}, *p;
 
-			obj = fake_dma_object(vm->i915, full_size);
-			if (IS_ERR(obj))
+			vma = fake_vma(vm, full_size);
+			if (IS_ERR(vma))
 				break;
 
-			list_add(&obj->st_link, &objects);
+			list_add(&vma->st_link, &vmas);
 
 			/* Align differing sized objects against the edges, and
 			 * check we don't walk off into the void when binding
@@ -409,21 +414,18 @@ static int fill_hole(struct i915_address_space *vm,
 				u64 offset;
 
 				offset = p->offset;
-				list_for_each_entry(obj, &objects, st_link) {
-					vma = i915_vma_instance(obj, vm, NULL);
-					if (IS_ERR(vma))
-						continue;
-
+				list_for_each_entry(vma, &vmas, st_link) {
 					if (p->step < 0) {
-						if (offset < hole_start + obj->base.size)
+						if (offset < hole_start + vma->size)
 							break;
-						offset -= obj->base.size;
+						offset -= vma->size;
 					}
 
 					err = i915_vma_pin(vma, 0, 0, offset | flags);
 					if (err) {
-						pr_err("%s(%s) pin (forward) failed with err=%d on size=%lu pages (prime=%lu), offset=%llx\n",
-						       __func__, p->name, err, npages, prime, offset);
+						pr_err("%s(%s) pin (forward) failed with err=%d on size=%lu pages (prime=%lu), offset=%llx, hole [%llx, %llx]\n",
+						       __func__, p->name, err, npages, prime, offset,
+						       hole_start, hole_end);
 						goto err;
 					}
 
@@ -439,22 +441,18 @@ static int fill_hole(struct i915_address_space *vm,
 					i915_vma_unpin(vma);
 
 					if (p->step > 0) {
-						if (offset + obj->base.size > hole_end)
+						if (offset + vma->node.size > hole_end)
 							break;
-						offset += obj->base.size;
+						offset += vma->node.size;
 					}
 				}
 
 				offset = p->offset;
-				list_for_each_entry(obj, &objects, st_link) {
-					vma = i915_vma_instance(obj, vm, NULL);
-					if (IS_ERR(vma))
-						continue;
-
+				list_for_each_entry(vma, &vmas, st_link) {
 					if (p->step < 0) {
-						if (offset < hole_start + obj->base.size)
+						if (offset < hole_start + vma->node.size)
 							break;
-						offset -= obj->base.size;
+						offset -= vma->node.size;
 					}
 
 					if (!drm_mm_node_allocated(&vma->node) ||
@@ -475,22 +473,18 @@ static int fill_hole(struct i915_address_space *vm,
 					}
 
 					if (p->step > 0) {
-						if (offset + obj->base.size > hole_end)
+						if (offset + vma->node.size > hole_end)
 							break;
-						offset += obj->base.size;
+						offset += vma->node.size;
 					}
 				}
 
 				offset = p->offset;
-				list_for_each_entry_reverse(obj, &objects, st_link) {
-					vma = i915_vma_instance(obj, vm, NULL);
-					if (IS_ERR(vma))
-						continue;
-
+				list_for_each_entry_reverse(vma, &vmas, st_link) {
 					if (p->step < 0) {
-						if (offset < hole_start + obj->base.size)
+						if (offset < hole_start + vma->node.size)
 							break;
-						offset -= obj->base.size;
+						offset -= vma->node.size;
 					}
 
 					err = i915_vma_pin(vma, 0, 0, offset | flags);
@@ -512,22 +506,18 @@ static int fill_hole(struct i915_address_space *vm,
 					i915_vma_unpin(vma);
 
 					if (p->step > 0) {
-						if (offset + obj->base.size > hole_end)
+						if (offset + vma->size >= hole_end)
 							break;
-						offset += obj->base.size;
+						offset += vma->size;
 					}
 				}
 
 				offset = p->offset;
-				list_for_each_entry_reverse(obj, &objects, st_link) {
-					vma = i915_vma_instance(obj, vm, NULL);
-					if (IS_ERR(vma))
-						continue;
-
+				list_for_each_entry_reverse(vma, &vmas, st_link) {
 					if (p->step < 0) {
-						if (offset < hole_start + obj->base.size)
+						if (offset < hole_start + vma->size)
 							break;
-						offset -= obj->base.size;
+						offset -= vma->size;
 					}
 
 					if (!drm_mm_node_allocated(&vma->node) ||
@@ -548,9 +538,9 @@ static int fill_hole(struct i915_address_space *vm,
 					}
 
 					if (p->step > 0) {
-						if (offset + obj->base.size > hole_end)
+						if (offset + vma->size >= hole_end)
 							break;
-						offset += obj->base.size;
+						offset += vma->size;
 					}
 				}
 			}
@@ -562,14 +552,14 @@ static int fill_hole(struct i915_address_space *vm,
 			}
 		}
 
-		close_object_list(&objects, vm);
+		close_vma_list(&vmas);
 		cleanup_freed_objects(vm->i915);
 	}
 
 	return 0;
 
 err:
-	close_object_list(&objects, vm);
+	close_vma_list(&vmas);
 	return err;
 }
 
@@ -590,24 +580,17 @@ static int walk_hole(struct i915_address_space *vm,
 		flags |= PIN_GLOBAL;
 
 	for_each_prime_number_from(size, 1, max_pages) {
-		struct drm_i915_gem_object *obj;
 		struct i915_vma *vma;
 		u64 addr;
 		int err = 0;
 
-		obj = fake_dma_object(vm->i915, size << PAGE_SHIFT);
-		if (IS_ERR(obj))
+		vma = fake_vma(vm, size << PAGE_SHIFT);
+		if (IS_ERR(vma))
 			break;
 
-		vma = i915_vma_instance(obj, vm, NULL);
-		if (IS_ERR(vma)) {
-			err = PTR_ERR(vma);
-			goto err_put;
-		}
-
 		for (addr = hole_start;
-		     addr + obj->base.size < hole_end;
-		     addr += obj->base.size) {
+		     addr + vma->size < hole_end;
+		     addr += vma->size) {
 			err = i915_vma_pin(vma, 0, 0, addr | flags);
 			if (err) {
 				pr_err("%s bind failed at %llx + %llx [hole %llx- %llx] with err=%d\n",
@@ -643,7 +626,7 @@ static int walk_hole(struct i915_address_space *vm,
 		}
 
 err_put:
-		i915_gem_object_put(obj);
+		i915_vma_put(vma);
 		if (err)
 			return err;
 
@@ -672,10 +655,9 @@ static int pot_hole(struct i915_address_space *vm,
 		return PTR_ERR(obj);
 
 	vma = i915_vma_instance(obj, vm, NULL);
-	if (IS_ERR(vma)) {
-		err = PTR_ERR(vma);
-		goto err_obj;
-	}
+	i915_gem_object_put(obj);
+	if (IS_ERR(vma))
+		return PTR_ERR(vma);
 
 	/* Insert a pair of pages across every pot boundary within the hole */
 	for (pot = fls64(hole_end - 1) - 1;
@@ -694,7 +676,7 @@ static int pot_hole(struct i915_address_space *vm,
 				       addr,
 				       hole_start, hole_end,
 				       err);
-				goto err_obj;
+				goto err_vma;
 			}
 
 			if (!drm_mm_node_allocated(&vma->node) ||
@@ -704,7 +686,7 @@ static int pot_hole(struct i915_address_space *vm,
 				i915_vma_unpin(vma);
 				err = i915_vma_unbind(vma);
 				err = -EINVAL;
-				goto err_obj;
+				goto err_vma;
 			}
 
 			i915_vma_unpin(vma);
@@ -716,12 +698,12 @@ static int pot_hole(struct i915_address_space *vm,
 				"%s timed out after %d/%d\n",
 				__func__, pot, fls64(hole_end - 1) - 1)) {
 			err = -EINTR;
-			goto err_obj;
+			goto err_vma;
 		}
 	}
 
-err_obj:
-	i915_gem_object_put(obj);
+err_vma:
+	i915_vma_put(vma);
 	return err;
 }
 
@@ -739,7 +721,6 @@ static int drunk_hole(struct i915_address_space *vm,
 
 	/* Keep creating larger objects until one cannot fit into the hole */
 	for (size = 12; (hole_end - hole_start) >> size; size++) {
-		struct drm_i915_gem_object *obj;
 		unsigned int *order, count, n;
 		struct i915_vma *vma;
 		u64 hole_size;
@@ -770,18 +751,12 @@ static int drunk_hole(struct i915_address_space *vm,
 		 * memory. We expect to hit -ENOMEM.
 		 */
 
-		obj = fake_dma_object(vm->i915, BIT_ULL(size));
-		if (IS_ERR(obj)) {
+		vma = fake_vma(vm, BIT_ULL(size));
+		if (IS_ERR(vma)) {
 			kfree(order);
 			break;
 		}
 
-		vma = i915_vma_instance(obj, vm, NULL);
-		if (IS_ERR(vma)) {
-			err = PTR_ERR(vma);
-			goto err_obj;
-		}
-
 		GEM_BUG_ON(vma->size != BIT_ULL(size));
 
 		for (n = 0; n < count; n++) {
@@ -794,7 +769,7 @@ static int drunk_hole(struct i915_address_space *vm,
 				       addr, BIT_ULL(size),
 				       hole_start, hole_end,
 				       err);
-				goto err_obj;
+				goto err_vma;
 			}
 
 			if (!drm_mm_node_allocated(&vma->node) ||
@@ -804,7 +779,7 @@ static int drunk_hole(struct i915_address_space *vm,
 				i915_vma_unpin(vma);
 				err = i915_vma_unbind(vma);
 				err = -EINVAL;
-				goto err_obj;
+				goto err_vma;
 			}
 
 			i915_vma_unpin(vma);
@@ -815,12 +790,12 @@ static int drunk_hole(struct i915_address_space *vm,
 					"%s timed out after %d/%d\n",
 					__func__, n, count)) {
 				err = -EINTR;
-				goto err_obj;
+				goto err_vma;
 			}
 		}
 
-err_obj:
-		i915_gem_object_put(obj);
+err_vma:
+		i915_vma_put(vma);
 		kfree(order);
 		if (err)
 			return err;
@@ -835,10 +810,9 @@ static int __shrink_hole(struct i915_address_space *vm,
 			 u64 hole_start, u64 hole_end,
 			 unsigned long end_time)
 {
-	struct drm_i915_gem_object *obj;
 	unsigned long flags = PIN_OFFSET_FIXED | PIN_USER;
 	unsigned int order = 12;
-	LIST_HEAD(objects);
+	LIST_HEAD(vmas);
 	int err = 0;
 	u64 addr;
 
@@ -848,19 +822,12 @@ static int __shrink_hole(struct i915_address_space *vm,
 		u64 size = BIT_ULL(order++);
 
 		size = min(size, hole_end - addr);
-		obj = fake_dma_object(vm->i915, size);
-		if (IS_ERR(obj)) {
-			err = PTR_ERR(obj);
-			break;
-		}
-
-		list_add(&obj->st_link, &objects);
-
-		vma = i915_vma_instance(obj, vm, NULL);
+		vma = fake_vma(vm, size);
 		if (IS_ERR(vma)) {
 			err = PTR_ERR(vma);
 			break;
 		}
+		list_add(&vma->st_link, &vmas);
 
 		GEM_BUG_ON(vma->size != size);
 
@@ -901,7 +868,7 @@ static int __shrink_hole(struct i915_address_space *vm,
 		}
 	}
 
-	close_object_list(&objects, vm);
+	close_vma_list(&vmas);
 	cleanup_freed_objects(vm->i915);
 	return err;
 }
@@ -933,8 +900,7 @@ static int shrink_boom(struct i915_address_space *vm,
 		       unsigned long end_time)
 {
 	unsigned int sizes[] = { SZ_2M, SZ_1G };
-	struct drm_i915_gem_object *purge;
-	struct drm_i915_gem_object *explode;
+	struct i915_vma *purge, *explode;
 	int err;
 	int i;
 
@@ -948,26 +914,19 @@ static int shrink_boom(struct i915_address_space *vm,
 	for (i = 0; i < ARRAY_SIZE(sizes); ++i) {
 		unsigned int flags = PIN_USER | PIN_OFFSET_FIXED;
 		unsigned int size = sizes[i];
-		struct i915_vma *vma;
 
-		purge = fake_dma_object(vm->i915, size);
+		purge = fake_vma(vm, size);
 		if (IS_ERR(purge))
 			return PTR_ERR(purge);
 
-		vma = i915_vma_instance(purge, vm, NULL);
-		if (IS_ERR(vma)) {
-			err = PTR_ERR(vma);
-			goto err_purge;
-		}
-
-		err = i915_vma_pin(vma, 0, 0, flags);
+		err = i915_vma_pin(purge, 0, 0, flags);
 		if (err)
 			goto err_purge;
 
 		/* Should now be ripe for purging */
-		i915_vma_unpin(vma);
+		i915_vma_unpin(purge);
 
-		explode = fake_dma_object(vm->i915, size);
+		explode = fake_vma(vm, size);
 		if (IS_ERR(explode)) {
 			err = PTR_ERR(explode);
 			goto err_purge;
@@ -977,20 +936,14 @@ static int shrink_boom(struct i915_address_space *vm,
 		vm->fault_attr.interval = 1;
 		atomic_set(&vm->fault_attr.times, -1);
 
-		vma = i915_vma_instance(explode, vm, NULL);
-		if (IS_ERR(vma)) {
-			err = PTR_ERR(vma);
-			goto err_explode;
-		}
-
-		err = i915_vma_pin(vma, 0, 0, flags | size);
+		err = i915_vma_pin(explode, 0, 0, flags | size);
 		if (err)
 			goto err_explode;
 
-		i915_vma_unpin(vma);
+		i915_vma_unpin(explode);
 
-		i915_gem_object_put(purge);
-		i915_gem_object_put(explode);
+		i915_vma_put(purge);
+		i915_vma_put(explode);
 
 		memset(&vm->fault_attr, 0, sizeof(vm->fault_attr));
 		cleanup_freed_objects(vm->i915);
@@ -999,9 +952,9 @@ static int shrink_boom(struct i915_address_space *vm,
 	return 0;
 
 err_explode:
-	i915_gem_object_put(explode);
+	i915_vma_put(explode);
 err_purge:
-	i915_gem_object_put(purge);
+	i915_vma_put(purge);
 	memset(&vm->fault_attr, 0, sizeof(vm->fault_attr));
 	return err;
 }
@@ -1308,9 +1261,9 @@ static int igt_mock_drunk(void *arg)
 static int igt_gtt_reserve(void *arg)
 {
 	struct i915_ggtt *ggtt = arg;
-	struct drm_i915_gem_object *obj, *on;
+	struct i915_vma *vma, *vn;
 	I915_RND_STATE(prng);
-	LIST_HEAD(objects);
+	LIST_HEAD(vmas);
 	u64 total;
 	int err = -ENODEV;
 
@@ -1323,7 +1276,7 @@ static int igt_gtt_reserve(void *arg)
 	for (total = 0;
 	     total + 2 * I915_GTT_PAGE_SIZE <= ggtt->vm.total;
 	     total += 2 * I915_GTT_PAGE_SIZE) {
-		struct i915_vma *vma;
+		struct drm_i915_gem_object *obj;
 
 		obj = i915_gem_object_create_internal(ggtt->vm.i915,
 						      2 * PAGE_SIZE);
@@ -1338,17 +1291,17 @@ static int igt_gtt_reserve(void *arg)
 			goto out;
 		}
 
-		list_add(&obj->st_link, &objects);
-
 		vma = i915_vma_instance(obj, &ggtt->vm, NULL);
+		i915_gem_object_put(obj);
 		if (IS_ERR(vma)) {
 			err = PTR_ERR(vma);
 			goto out;
 		}
+		list_add(&vma->st_link, &vmas);
 
 		mutex_lock(&ggtt->vm.mutex);
 		err = i915_gem_gtt_reserve(&ggtt->vm, &vma->node,
-					   obj->base.size,
+					   vma->size,
 					   total,
 					   obj->cache_level,
 					   0);
@@ -1375,7 +1328,7 @@ static int igt_gtt_reserve(void *arg)
 	for (total = I915_GTT_PAGE_SIZE;
 	     total + 2 * I915_GTT_PAGE_SIZE <= ggtt->vm.total;
 	     total += 2 * I915_GTT_PAGE_SIZE) {
-		struct i915_vma *vma;
+		struct drm_i915_gem_object *obj;
 
 		obj = i915_gem_object_create_internal(ggtt->vm.i915,
 						      2 * PAGE_SIZE);
@@ -1390,17 +1343,17 @@ static int igt_gtt_reserve(void *arg)
 			goto out;
 		}
 
-		list_add(&obj->st_link, &objects);
-
 		vma = i915_vma_instance(obj, &ggtt->vm, NULL);
+		i915_gem_object_put(obj);
 		if (IS_ERR(vma)) {
 			err = PTR_ERR(vma);
 			goto out;
 		}
+		list_add(&vma->st_link, &vmas);
 
 		mutex_lock(&ggtt->vm.mutex);
 		err = i915_gem_gtt_reserve(&ggtt->vm, &vma->node,
-					   obj->base.size,
+					   vma->size,
 					   total,
 					   obj->cache_level,
 					   0);
@@ -1424,16 +1377,9 @@ static int igt_gtt_reserve(void *arg)
 	}
 
 	/* And then try at random */
-	list_for_each_entry_safe(obj, on, &objects, st_link) {
-		struct i915_vma *vma;
+	list_for_each_entry_safe(vma, vn, &vmas, st_link) {
 		u64 offset;
 
-		vma = i915_vma_instance(obj, &ggtt->vm, NULL);
-		if (IS_ERR(vma)) {
-			err = PTR_ERR(vma);
-			goto out;
-		}
-
 		err = i915_vma_unbind(vma);
 		if (err) {
 			pr_err("i915_vma_unbind failed with err=%d!\n", err);
@@ -1447,9 +1393,9 @@ static int igt_gtt_reserve(void *arg)
 
 		mutex_lock(&ggtt->vm.mutex);
 		err = i915_gem_gtt_reserve(&ggtt->vm, &vma->node,
-					   obj->base.size,
+					   vma->size,
 					   offset,
-					   obj->cache_level,
+					   vma->node.color,
 					   0);
 		mutex_unlock(&ggtt->vm.mutex);
 		if (err) {
@@ -1471,17 +1417,14 @@ static int igt_gtt_reserve(void *arg)
 	}
 
 out:
-	list_for_each_entry_safe(obj, on, &objects, st_link) {
-		i915_gem_object_unpin_pages(obj);
-		i915_gem_object_put(obj);
-	}
+	list_for_each_entry_safe(vma, vn, &vmas, st_link)
+		i915_vma_put(vma);
 	return err;
 }
 
 static int igt_gtt_insert(void *arg)
 {
 	struct i915_ggtt *ggtt = arg;
-	struct drm_i915_gem_object *obj, *on;
 	struct drm_mm_node tmp = {};
 	const struct invalid_insert {
 		u64 size;
@@ -1510,7 +1453,8 @@ static int igt_gtt_insert(void *arg)
 		},
 		{}
 	}, *ii;
-	LIST_HEAD(objects);
+	struct i915_vma *vma, *vn;
+	LIST_HEAD(vmas);
 	u64 total;
 	int err = -ENODEV;
 
@@ -1539,7 +1483,7 @@ static int igt_gtt_insert(void *arg)
 	for (total = 0;
 	     total + I915_GTT_PAGE_SIZE <= ggtt->vm.total;
 	     total += I915_GTT_PAGE_SIZE) {
-		struct i915_vma *vma;
+		struct drm_i915_gem_object *obj;
 
 		obj = i915_gem_object_create_internal(ggtt->vm.i915,
 						      I915_GTT_PAGE_SIZE);
@@ -1554,13 +1498,13 @@ static int igt_gtt_insert(void *arg)
 			goto out;
 		}
 
-		list_add(&obj->st_link, &objects);
-
 		vma = i915_vma_instance(obj, &ggtt->vm, NULL);
+		i915_gem_object_put(obj);
 		if (IS_ERR(vma)) {
 			err = PTR_ERR(vma);
 			goto out;
 		}
+		list_add(&vma->st_link, &vmas);
 
 		mutex_lock(&ggtt->vm.mutex);
 		err = i915_gem_gtt_insert(&ggtt->vm, &vma->node,
@@ -1570,7 +1514,6 @@ static int igt_gtt_insert(void *arg)
 		mutex_unlock(&ggtt->vm.mutex);
 		if (err == -ENOSPC) {
 			/* maxed out the GGTT space */
-			i915_gem_object_put(obj);
 			break;
 		}
 		if (err) {
@@ -1584,15 +1527,7 @@ static int igt_gtt_insert(void *arg)
 		GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
 	}
 
-	list_for_each_entry(obj, &objects, st_link) {
-		struct i915_vma *vma;
-
-		vma = i915_vma_instance(obj, &ggtt->vm, NULL);
-		if (IS_ERR(vma)) {
-			err = PTR_ERR(vma);
-			goto out;
-		}
-
+	list_for_each_entry_safe(vma, vn, &vmas, st_link) {
 		if (!drm_mm_node_allocated(&vma->node)) {
 			pr_err("VMA was unexpectedly evicted!\n");
 			err = -EINVAL;
@@ -1603,16 +1538,9 @@ static int igt_gtt_insert(void *arg)
 	}
 
 	/* If we then reinsert, we should find the same hole */
-	list_for_each_entry_safe(obj, on, &objects, st_link) {
-		struct i915_vma *vma;
+	list_for_each_entry_safe(vma, vn, &vmas, st_link) {
 		u64 offset;
 
-		vma = i915_vma_instance(obj, &ggtt->vm, NULL);
-		if (IS_ERR(vma)) {
-			err = PTR_ERR(vma);
-			goto out;
-		}
-
 		GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
 		offset = vma->node.start;
 
@@ -1624,7 +1552,7 @@ static int igt_gtt_insert(void *arg)
 
 		mutex_lock(&ggtt->vm.mutex);
 		err = i915_gem_gtt_insert(&ggtt->vm, &vma->node,
-					  obj->base.size, 0, obj->cache_level,
+					  vma->node.size, 0, vma->node.color,
 					  0, ggtt->vm.total,
 					  0);
 		mutex_unlock(&ggtt->vm.mutex);
@@ -1648,7 +1576,7 @@ static int igt_gtt_insert(void *arg)
 	for (total = 0;
 	     total + 2 * I915_GTT_PAGE_SIZE <= ggtt->vm.total;
 	     total += 2 * I915_GTT_PAGE_SIZE) {
-		struct i915_vma *vma;
+		struct drm_i915_gem_object *obj;
 
 		obj = i915_gem_object_create_internal(ggtt->vm.i915,
 						      2 * I915_GTT_PAGE_SIZE);
@@ -1663,13 +1591,13 @@ static int igt_gtt_insert(void *arg)
 			goto out;
 		}
 
-		list_add(&obj->st_link, &objects);
-
 		vma = i915_vma_instance(obj, &ggtt->vm, NULL);
+		i915_gem_object_put(obj);
 		if (IS_ERR(vma)) {
 			err = PTR_ERR(vma);
 			goto out;
 		}
+		list_add(&vma->st_link, &vmas);
 
 		mutex_lock(&ggtt->vm.mutex);
 		err = i915_gem_gtt_insert(&ggtt->vm, &vma->node,
@@ -1688,10 +1616,8 @@ static int igt_gtt_insert(void *arg)
 	}
 
 out:
-	list_for_each_entry_safe(obj, on, &objects, st_link) {
-		i915_gem_object_unpin_pages(obj);
-		i915_gem_object_put(obj);
-	}
+	list_for_each_entry_safe(vma, vn, &vmas, st_link)
+		i915_vma_put(vma);
 	return err;
 }
 
@@ -1878,6 +1804,7 @@ static int igt_cs_tlb(void *arg)
 	i915_gem_object_set_cache_coherency(out, I915_CACHING_CACHED);
 
 	vma = i915_vma_instance(out, vm, NULL);
+	i915_gem_object_put(out);
 	if (IS_ERR(vma)) {
 		err = PTR_ERR(vma);
 		goto out_put_batch;
@@ -1888,13 +1815,13 @@ static int igt_cs_tlb(void *arg)
 			   PIN_OFFSET_FIXED |
 			   (vm->total - PAGE_SIZE));
 	if (err)
-		goto out_put_out;
+		goto out_put_vma;
 	GEM_BUG_ON(vma->node.start != vm->total - PAGE_SIZE);
 
 	result = i915_gem_object_pin_map(out, I915_MAP_WB);
 	if (IS_ERR(result)) {
 		err = PTR_ERR(result);
-		goto out_put_out;
+		goto out_put_vma;
 	}
 
 	for_each_gem_engine(ce, i915_gem_context_lock_engines(ctx), it) {
@@ -1907,6 +1834,7 @@ static int igt_cs_tlb(void *arg)
 		while (!__igt_timeout(end_time, NULL)) {
 			struct i915_vm_pt_stash stash = {};
 			struct i915_request *rq;
+			struct i915_vma *v;
 			u64 offset;
 
 			offset = igt_random_offset(&prng,
@@ -1915,13 +1843,13 @@ static int igt_cs_tlb(void *arg)
 
 			memset32(result, STACK_MAGIC, PAGE_SIZE / sizeof(u32));
 
-			vma = i915_vma_instance(bbe, vm, NULL);
-			if (IS_ERR(vma)) {
-				err = PTR_ERR(vma);
+			v = i915_vma_instance(bbe, vm, NULL);
+			if (IS_ERR(v)) {
+				err = PTR_ERR(v);
 				goto end;
 			}
 
-			err = vma->ops->set_pages(vma);
+			err = v->ops->set_pages(v);
 			if (err)
 				goto end;
 
@@ -1941,10 +1869,10 @@ static int igt_cs_tlb(void *arg)
 
 			/* Prime the TLB with the dummy pages */
 			for (i = 0; i < count; i++) {
-				vma->node.start = offset + i * PAGE_SIZE;
-				vm->insert_entries(vm, vma, I915_CACHE_NONE, 0);
+				v->node.start = offset + i * PAGE_SIZE;
+				vm->insert_entries(vm, v, I915_CACHE_NONE, 0);
 
-				rq = submit_batch(ce, vma->node.start);
+				rq = submit_batch(ce, v->node.start);
 				if (IS_ERR(rq)) {
 					err = PTR_ERR(rq);
 					goto end;
@@ -1952,7 +1880,8 @@ static int igt_cs_tlb(void *arg)
 				i915_request_put(rq);
 			}
 
-			vma->ops->clear_pages(vma);
+			v->ops->clear_pages(v);
+			i915_vma_put(v);
 
 			err = context_sync(ce);
 			if (err) {
@@ -1961,13 +1890,13 @@ static int igt_cs_tlb(void *arg)
 				goto end;
 			}
 
-			vma = i915_vma_instance(act, vm, NULL);
-			if (IS_ERR(vma)) {
-				err = PTR_ERR(vma);
+			v = i915_vma_instance(act, vm, NULL);
+			if (IS_ERR(v)) {
+				err = PTR_ERR(v);
 				goto end;
 			}
 
-			err = vma->ops->set_pages(vma);
+			err = v->ops->set_pages(v);
 			if (err)
 				goto end;
 
@@ -1977,10 +1906,10 @@ static int igt_cs_tlb(void *arg)
 				u32 *cs = batch + i * 64 / sizeof(*cs);
 				u64 addr;
 
-				vma->node.start = offset + i * PAGE_SIZE;
-				vm->insert_entries(vm, vma, I915_CACHE_NONE, 0);
+				v->node.start = offset + i * PAGE_SIZE;
+				vm->insert_entries(vm, v, I915_CACHE_NONE, 0);
 
-				addr = vma->node.start + i * 64;
+				addr = v->node.start + i * 64;
 				cs[4] = MI_NOOP;
 				cs[6] = lower_32_bits(addr);
 				cs[7] = upper_32_bits(addr);
@@ -2005,7 +1934,8 @@ static int igt_cs_tlb(void *arg)
 			}
 			end_spin(batch, count - 1);
 
-			vma->ops->clear_pages(vma);
+			v->ops->clear_pages(v);
+			i915_vma_put(v);
 
 			err = context_sync(ce);
 			if (err) {
@@ -2033,8 +1963,8 @@ static int igt_cs_tlb(void *arg)
 		err = -EIO;
 	i915_gem_context_unlock_engines(ctx);
 	i915_gem_object_unpin_map(out);
-out_put_out:
-	i915_gem_object_put(out);
+out_put_vma:
+	i915_vma_put(vma);
 out_put_batch:
 	i915_gem_object_unpin_map(act);
 out_put_act:
diff --git a/drivers/gpu/drm/i915/selftests/i915_request.c b/drivers/gpu/drm/i915/selftests/i915_request.c
index 9271aad7f779..e87c7e52f4cc 100644
--- a/drivers/gpu/drm/i915/selftests/i915_request.c
+++ b/drivers/gpu/drm/i915/selftests/i915_request.c
@@ -621,8 +621,8 @@ static struct i915_vma *empty_batch(struct drm_i915_private *i915)
 
 	cmd = i915_gem_object_pin_map(obj, I915_MAP_WB);
 	if (IS_ERR(cmd)) {
-		err = PTR_ERR(cmd);
-		goto err;
+		i915_gem_object_put(obj);
+		return ERR_CAST(cmd);
 	}
 
 	*cmd = MI_BATCH_BUFFER_END;
@@ -633,10 +633,9 @@ static struct i915_vma *empty_batch(struct drm_i915_private *i915)
 	intel_gt_chipset_flush(&i915->gt);
 
 	vma = i915_vma_instance(obj, &i915->ggtt.vm, NULL);
-	if (IS_ERR(vma)) {
-		err = PTR_ERR(vma);
-		goto err;
-	}
+	i915_gem_object_put(obj);
+	if (IS_ERR(vma))
+		return vma;
 
 	err = i915_vma_pin(vma, 0, 0, PIN_USER | PIN_GLOBAL);
 	if (err)
@@ -652,7 +651,7 @@ static struct i915_vma *empty_batch(struct drm_i915_private *i915)
 err_pin:
 	i915_vma_unpin(vma);
 err:
-	i915_gem_object_put(obj);
+	i915_vma_put(vma);
 	return ERR_PTR(err);
 }
 
@@ -772,6 +771,7 @@ static struct i915_vma *recursive_batch(struct drm_i915_private *i915)
 		return ERR_CAST(obj);
 
 	vma = i915_vma_instance(obj, i915->gt.vm, NULL);
+	i915_gem_object_put(obj);
 	if (IS_ERR(vma)) {
 		err = PTR_ERR(vma);
 		goto err;
@@ -808,7 +808,7 @@ static struct i915_vma *recursive_batch(struct drm_i915_private *i915)
 	return vma;
 
 err:
-	i915_gem_object_put(obj);
+	i915_vma_put(vma);
 	return ERR_PTR(err);
 }
 
diff --git a/drivers/gpu/drm/i915/selftests/i915_vma.c b/drivers/gpu/drm/i915/selftests/i915_vma.c
index af89c7fc8f59..34b356c7be8d 100644
--- a/drivers/gpu/drm/i915/selftests/i915_vma.c
+++ b/drivers/gpu/drm/i915/selftests/i915_vma.c
@@ -109,36 +109,34 @@ static int create_vmas(struct drm_i915_private *i915,
 {
 	struct drm_i915_gem_object *obj;
 	struct i915_gem_context *ctx;
-	int pinned;
 
 	list_for_each_entry(obj, objects, st_link) {
-		for (pinned = 0; pinned <= 1; pinned++) {
-			list_for_each_entry(ctx, contexts, link) {
-				struct i915_address_space *vm;
-				struct i915_vma *vma;
-				int err;
-
-				vm = i915_gem_context_get_vm_rcu(ctx);
-				vma = checked_vma_instance(obj, vm, NULL);
-				i915_vm_put(vm);
-				if (IS_ERR(vma))
-					return PTR_ERR(vma);
-
-				if (!assert_vma(vma, obj, ctx)) {
-					pr_err("VMA lookup/create failed\n");
-					return -EINVAL;
-				}
+		list_for_each_entry(ctx, contexts, link) {
+			struct i915_address_space *vm;
+			struct i915_vma *vma;
+			int err;
 
-				if (!pinned) {
-					err = i915_vma_pin(vma, 0, 0, PIN_USER);
-					if (err) {
-						pr_err("Failed to pin VMA\n");
-						return err;
-					}
-				} else {
-					i915_vma_unpin(vma);
-				}
+			vm = i915_gem_context_get_vm_rcu(ctx);
+			vma = checked_vma_instance(obj, vm, NULL);
+			i915_vm_put(vm);
+			if (IS_ERR(vma))
+				return PTR_ERR(vma);
+
+			if (!assert_vma(vma, obj, ctx)) {
+				pr_err("VMA lookup/create failed\n");
+				i915_vma_put(vma);
+				return -EINVAL;
 			}
+
+			err = i915_vma_pin(vma, 0, 0, PIN_USER);
+			if (err) {
+				pr_err("Failed to pin VMA\n");
+				i915_vma_put(vma);
+				return err;
+			}
+
+			i915_vma_unpin(vma);
+			i915_vma_put(vma);
 		}
 	}
 
@@ -322,8 +320,9 @@ static int igt_vma_pin1(void *arg)
 		return PTR_ERR(obj);
 
 	vma = checked_vma_instance(obj, &ggtt->vm, NULL);
+	i915_gem_object_put(obj);
 	if (IS_ERR(vma))
-		goto out;
+		return PTR_ERR(vma);
 
 	for (m = modes; m->assert; m++) {
 		err = i915_vma_pin(vma, m->size, 0, m->flags);
@@ -352,7 +351,7 @@ static int igt_vma_pin1(void *arg)
 
 	err = 0;
 out:
-	i915_gem_object_put(obj);
+	i915_vma_put(vma);
 	return err;
 }
 
@@ -502,6 +501,7 @@ static int igt_vma_rotate_remap(void *arg)
 		0,
 	}, *t;
 	const unsigned int max_pages = 64;
+	struct i915_vma *vma;
 	int err = -ENOMEM;
 
 	/* Create VMA for many different combinations of planes and check
@@ -530,18 +530,17 @@ static int igt_vma_rotate_remap(void *arg)
 			for_each_prime_number_from(view.rotated.plane[0].offset, 0, max_offset) {
 				for_each_prime_number_from(view.rotated.plane[1].offset, 0, max_offset) {
 					struct scatterlist *sg;
-					struct i915_vma *vma;
 
 					vma = checked_vma_instance(obj, vm, &view);
 					if (IS_ERR(vma)) {
 						err = PTR_ERR(vma);
-						goto out_object;
+						goto out_vma;
 					}
 
 					err = i915_vma_pin(vma, 0, 0, PIN_GLOBAL);
 					if (err) {
 						pr_err("Failed to pin VMA, err=%d\n", err);
-						goto out_object;
+						goto out_vma;
 					}
 
 					if (view.type == I915_GGTT_VIEW_ROTATED &&
@@ -549,7 +548,7 @@ static int igt_vma_rotate_remap(void *arg)
 						pr_err("VMA is wrong size, expected %lu, found %llu\n",
 						       PAGE_SIZE * rotated_size(a, b), vma->size);
 						err = -EINVAL;
-						goto out_object;
+						goto out_vma;
 					}
 
 					if (view.type == I915_GGTT_VIEW_REMAPPED &&
@@ -557,27 +556,27 @@ static int igt_vma_rotate_remap(void *arg)
 						pr_err("VMA is wrong size, expected %lu, found %llu\n",
 						       PAGE_SIZE * rotated_size(a, b), vma->size);
 						err = -EINVAL;
-						goto out_object;
+						goto out_vma;
 					}
 
 					if (vma->pages->nents > rotated_size(a, b)) {
 						pr_err("sg table is wrong sizeo, expected %u, found %u nents\n",
 						       rotated_size(a, b), vma->pages->nents);
 						err = -EINVAL;
-						goto out_object;
+						goto out_vma;
 					}
 
 					if (vma->node.size < vma->size) {
 						pr_err("VMA binding too small, expected %llu, found %llu\n",
 						       vma->size, vma->node.size);
 						err = -EINVAL;
-						goto out_object;
+						goto out_vma;
 					}
 
 					if (vma->pages == obj->mm.pages) {
 						pr_err("VMA using unrotated object pages!\n");
 						err = -EINVAL;
-						goto out_object;
+						goto out_vma;
 					}
 
 					sg = vma->pages->sgl;
@@ -599,11 +598,12 @@ static int igt_vma_rotate_remap(void *arg)
 							       view.rotated.plane[1].stride,
 							       view.rotated.plane[1].offset);
 							err = -EINVAL;
-							goto out_object;
+							goto out_vma;
 						}
 					}
 
 					i915_vma_unpin(vma);
+					i915_vma_put(vma);
 
 					cond_resched();
 				}
@@ -611,7 +611,10 @@ static int igt_vma_rotate_remap(void *arg)
 		}
 	}
 	}
+	goto out_object;
 
+out_vma:
+	i915_vma_put(vma);
 out_object:
 	i915_gem_object_put(obj);
 out:
@@ -709,7 +712,7 @@ static int igt_vma_partial(void *arg)
 		{ },
 	}, *p;
 	unsigned int sz, offset;
-	struct i915_vma *vma;
+	struct i915_vma *vma, *vn;
 	int err = -ENOMEM;
 
 	/* Create lots of different VMA for the object and check that
@@ -784,16 +787,20 @@ static int igt_vma_partial(void *arg)
 		}
 
 		err = i915_vma_pin(vma, 0, 0, PIN_GLOBAL);
-		if (err)
+		if (err) {
+			i915_vma_put(vma);
 			goto out_object;
+		}
 
 		if (!assert_pin(vma, NULL, obj->base.size, p->name)) {
 			pr_err("(%s) inconsistent full pin\n", p->name);
 			err = -EINVAL;
+			i915_vma_put(vma);
 			goto out_object;
 		}
 
 		i915_vma_unpin(vma);
+		i915_vma_put(vma);
 
 		count = 0;
 		list_for_each_entry(vma, &obj->vma.list, obj_link)
@@ -804,8 +811,13 @@ static int igt_vma_partial(void *arg)
 			goto out_object;
 		}
 	}
+	p--;
 
 out_object:
+	do {
+		list_for_each_entry_safe(vma, vn, &obj->vma.list, obj_link)
+			i915_vma_put(vma);
+	} while (p-- != phases);
 	i915_gem_object_put(obj);
 out:
 	return err;
@@ -910,6 +922,7 @@ static int igt_vma_remapped_gtt(void *arg)
 			i915_vma_unpin(vma);
 			if (IS_ERR(map)) {
 				err = PTR_ERR(map);
+				i915_vma_put(vma);
 				goto out;
 			}
 
@@ -928,6 +941,7 @@ static int igt_vma_remapped_gtt(void *arg)
 			}
 
 			i915_vma_unpin_iomap(vma);
+			i915_vma_put(vma);
 
 			vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0, PIN_MAPPABLE);
 			if (IS_ERR(vma)) {
@@ -941,6 +955,7 @@ static int igt_vma_remapped_gtt(void *arg)
 			i915_vma_unpin(vma);
 			if (IS_ERR(map)) {
 				err = PTR_ERR(map);
+				i915_vma_put(vma);
 				goto out;
 			}
 
@@ -967,6 +982,7 @@ static int igt_vma_remapped_gtt(void *arg)
 				}
 			}
 			i915_vma_unpin_iomap(vma);
+			i915_vma_put(vma);
 
 			cond_resched();
 		}
diff --git a/drivers/gpu/drm/i915/selftests/igt_spinner.c b/drivers/gpu/drm/i915/selftests/igt_spinner.c
index ec0ecb4e4ca6..62f074ddc301 100644
--- a/drivers/gpu/drm/i915/selftests/igt_spinner.c
+++ b/drivers/gpu/drm/i915/selftests/igt_spinner.c
@@ -107,12 +107,14 @@ igt_spinner_create_request(struct igt_spinner *spin,
 		return ERR_CAST(vma);
 
 	hws = i915_vma_instance(spin->hws, ce->vm, NULL);
-	if (IS_ERR(hws))
-		return ERR_CAST(hws);
+	if (IS_ERR(hws)) {
+		err = PTR_ERR(hws);
+		goto put_vma;
+	}
 
 	err = i915_vma_pin(vma, 0, 0, PIN_USER);
 	if (err)
-		return ERR_PTR(err);
+		goto put_hws;
 
 	err = i915_vma_pin(hws, 0, 0, PIN_USER);
 	if (err)
@@ -189,6 +191,10 @@ igt_spinner_create_request(struct igt_spinner *spin,
 	i915_vma_unpin(hws);
 unpin_vma:
 	i915_vma_unpin(vma);
+put_hws:
+	i915_vma_put(hws);
+put_vma:
+	i915_vma_put(vma);
 	return err ? ERR_PTR(err) : rq;
 }
 
diff --git a/drivers/gpu/drm/i915/selftests/intel_memory_region.c b/drivers/gpu/drm/i915/selftests/intel_memory_region.c
index 8d9fdf591514..f37c92720861 100644
--- a/drivers/gpu/drm/i915/selftests/intel_memory_region.c
+++ b/drivers/gpu/drm/i915/selftests/intel_memory_region.c
@@ -350,7 +350,7 @@ static int igt_gpu_write(struct i915_gem_context *ctx,
 
 	err = i915_vma_pin(vma, 0, 0, PIN_USER);
 	if (err)
-		goto out_free;
+		goto out_vma;
 
 	i = 0;
 	engines = i915_gem_context_lock_engines(ctx);
@@ -373,6 +373,9 @@ static int igt_gpu_write(struct i915_gem_context *ctx,
 	} while (!__igt_timeout(end_time, NULL));
 	i915_gem_context_unlock_engines(ctx);
 
+	i915_vma_unpin(vma);
+out_vma:
+	i915_vma_put(vma);
 out_free:
 	kfree(order);
 
diff --git a/drivers/gpu/drm/i915/selftests/mock_gem_device.c b/drivers/gpu/drm/i915/selftests/mock_gem_device.c
index 9b105b811f1f..32d80ead21ff 100644
--- a/drivers/gpu/drm/i915/selftests/mock_gem_device.c
+++ b/drivers/gpu/drm/i915/selftests/mock_gem_device.c
@@ -53,6 +53,30 @@ void mock_device_flush(struct drm_i915_private *i915)
 	} while (intel_gt_retire_requests_timeout(gt, MAX_SCHEDULE_TIMEOUT));
 }
 
+static int workqueues_init(struct drm_i915_private *i915)
+{
+	i915->wq = alloc_ordered_workqueue("i915", 0);
+	if (!i915->wq)
+		goto out_err;
+
+	i915->reclaim_wq = create_workqueue("i915-reclaim");
+	if (!i915->reclaim_wq)
+		goto out_free_wq;
+
+	return 0;
+
+out_free_wq:
+	destroy_workqueue(i915->wq);
+out_err:
+	return -ENOMEM;
+}
+
+static void workqueues_cleanup(struct drm_i915_private *i915)
+{
+	destroy_workqueue(i915->reclaim_wq);
+	destroy_workqueue(i915->wq);
+}
+
 static void mock_device_release(struct drm_device *dev)
 {
 	struct drm_i915_private *i915 = to_i915(dev);
@@ -69,13 +93,13 @@ static void mock_device_release(struct drm_device *dev)
 	i915_gem_drain_freed_objects(i915);
 
 	mock_fini_ggtt(&i915->ggtt);
-	destroy_workqueue(i915->wq);
 
 	intel_gt_driver_late_release(&i915->gt);
 	intel_memory_regions_driver_release(i915);
 
 	drm_mode_config_cleanup(&i915->drm);
 
+	workqueues_cleanup(i915);
 out:
 	put_device(&i915->drm.pdev->dev);
 	i915->drm.pdev = NULL;
@@ -181,8 +205,7 @@ struct drm_i915_private *mock_gem_device(void)
 	atomic_inc(&i915->gt.wakeref.count); /* disable; no hw support */
 	i915->gt.awake = -ENODEV;
 
-	i915->wq = alloc_ordered_workqueue("mock", 0);
-	if (!i915->wq)
+	if (workqueues_init(i915))
 		goto err_drv;
 
 	mock_init_contexts(i915);
@@ -209,7 +232,7 @@ struct drm_i915_private *mock_gem_device(void)
 err_context:
 	intel_gt_driver_remove(&i915->gt);
 err_unlock:
-	destroy_workqueue(i915->wq);
+	workqueues_cleanup(i915);
 err_drv:
 	intel_gt_driver_late_release(&i915->gt);
 	intel_memory_regions_driver_release(i915);
diff --git a/drivers/gpu/drm/i915/selftests/mock_gtt.c b/drivers/gpu/drm/i915/selftests/mock_gtt.c
index 7270fc8ca801..df84bef932db 100644
--- a/drivers/gpu/drm/i915/selftests/mock_gtt.c
+++ b/drivers/gpu/drm/i915/selftests/mock_gtt.c
@@ -136,5 +136,6 @@ void mock_init_ggtt(struct drm_i915_private *i915, struct i915_ggtt *ggtt)
 
 void mock_fini_ggtt(struct i915_ggtt *ggtt)
 {
+	flush_work(&ggtt->vm.release.work);
 	i915_address_space_fini(&ggtt->vm);
 }
-- 
2.20.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [Intel-gfx] ✗ Fi.CI.CHECKPATCH: warning for series starting with [01/20] drm/i915: Preallocate stashes for vma page-directories
  2020-07-06  6:19 [Intel-gfx] s/obj->mm.lock// Chris Wilson
                   ` (19 preceding siblings ...)
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 20/20] drm/i915: Track i915_vma with its own reference counter Chris Wilson
@ 2020-07-06  6:28 ` Patchwork
  2020-07-06  6:29 ` [Intel-gfx] ✗ Fi.CI.SPARSE: " Patchwork
                   ` (3 subsequent siblings)
  24 siblings, 0 replies; 65+ messages in thread
From: Patchwork @ 2020-07-06  6:28 UTC (permalink / raw)
  To: Chris Wilson; +Cc: intel-gfx

== Series Details ==

Series: series starting with [01/20] drm/i915: Preallocate stashes for vma page-directories
URL   : https://patchwork.freedesktop.org/series/79129/
State : warning

== Summary ==

$ dim checkpatch origin/drm-tip
b5c5d0ce6d3d drm/i915: Preallocate stashes for vma page-directories
5ff5ae1fb079 drm/i915: Switch to object allocations for page directories
1da308be5b55 drm/i915/gem: Don't drop the timeline lock during execbuf
a8aa1e74f47e drm/i915/gem: Rename execbuf.bind_link to unbound_link
6779fcaed114 drm/i915/gem: Break apart the early i915_vma_pin from execbuf object lookup
c3cc3ce10691 drm/i915/gem: Remove the call for no-evict i915_vma_pin
acb85ac92a2f drm/i915: Add list_for_each_entry_safe_continue_reverse
-:21: CHECK:MACRO_ARG_REUSE: Macro argument reuse 'pos' - possible side-effects?
#21: FILE: drivers/gpu/drm/i915/i915_utils.h:269:
+#define list_for_each_entry_safe_continue_reverse(pos, n, head, member)	\
+	for (pos = list_prev_entry(pos, member),			\
+	     n = list_prev_entry(pos, member);				\
+	     &pos->member != (head);					\
+	     pos = n, n = list_prev_entry(n, member))

-:21: CHECK:MACRO_ARG_REUSE: Macro argument reuse 'n' - possible side-effects?
#21: FILE: drivers/gpu/drm/i915/i915_utils.h:269:
+#define list_for_each_entry_safe_continue_reverse(pos, n, head, member)	\
+	for (pos = list_prev_entry(pos, member),			\
+	     n = list_prev_entry(pos, member);				\
+	     &pos->member != (head);					\
+	     pos = n, n = list_prev_entry(n, member))

-:21: CHECK:MACRO_ARG_REUSE: Macro argument reuse 'member' - possible side-effects?
#21: FILE: drivers/gpu/drm/i915/i915_utils.h:269:
+#define list_for_each_entry_safe_continue_reverse(pos, n, head, member)	\
+	for (pos = list_prev_entry(pos, member),			\
+	     n = list_prev_entry(pos, member);				\
+	     &pos->member != (head);					\
+	     pos = n, n = list_prev_entry(n, member))

total: 0 errors, 0 warnings, 3 checks, 12 lines checked
ed90e5840625 drm/i915: Always defer fenced work to the worker
03e637530b30 drm/i915/gem: Assign context id for async work
fe694efd1728 drm/i915: Export a preallocate variant of i915_active_acquire()
8669de2e382e drm/i915/gem: Separate the ww_mutex walker into its own list
a1fb11db685d drm/i915/gem: Asynchronous GTT unbinding
92aa37aeda01 drm/i915/gem: Bind the fence async for execbuf
6002974eb6ce drm/i915/gem: Include cmdparser in common execbuf pinning
8b519af3185f drm/i915/gem: Include secure batch in common execbuf pinning
3a96506f63d3 drm/i915/gem: Reintroduce multiple passes for reloc processing
-:1434: WARNING:MEMORY_BARRIER: memory barrier without comment
#1434: FILE: drivers/gpu/drm/i915/gem/selftests/i915_gem_execbuffer.c:161:
+		wmb();

total: 0 errors, 1 warnings, 0 checks, 1421 lines checked
2036c3ef6f3e drm/i915: Add an implementation for i915_gem_ww_ctx locking, v2.
-:59: WARNING:FILE_PATH_CHANGES: added, moved or deleted file(s), does MAINTAINERS need updating?
#59: 
new file mode 100644

-:354: WARNING:LINE_SPACING: Missing a blank line after declarations
#354: FILE: drivers/gpu/drm/i915/mm/st_acquire_ctx.c:106:
+	const unsigned int total = ARRAY_SIZE(dl->obj);
+	I915_RND_STATE(prng);

-:450: WARNING:YIELD: Using yield() is generally wrong. See yield() kernel-doc (sched/core.c)
#450: FILE: drivers/gpu/drm/i915/mm/st_acquire_ctx.c:202:
+	yield(); /* start all threads before we begin */

total: 0 errors, 3 warnings, 0 checks, 446 lines checked
0c763806ba4f drm/i915/gem: Pull execbuf dma resv under a single critical section
f3622825131d drm/i915/gem: Replace i915_gem_object.mm.mutex with reservation_ww_class
33bcfafbc4ab drm/i915: Track i915_vma with its own reference counter
-:2081: CHECK:UNCOMMENTED_DEFINITION: spinlock_t definition without comment
#2081: FILE: drivers/gpu/drm/i915/gt/intel_gtt.h:254:
+		spinlock_t lock;

-:3963: CHECK:UNCOMMENTED_DEFINITION: spinlock_t definition without comment
#3963: FILE: drivers/gpu/drm/i915/i915_vma.h:392:
+	spinlock_t lock;

-:4210: WARNING:DEEP_INDENTATION: Too many leading tabs - consider code refactoring
#4210: FILE: drivers/gpu/drm/i915/selftests/i915_gem_gtt.c:419:
+						if (offset < hole_start + vma->size)

-:4221: WARNING:LONG_LINE: line length of 101 exceeds 100 columns
#4221: FILE: drivers/gpu/drm/i915/selftests/i915_gem_gtt.c:427:
+						       __func__, p->name, err, npages, prime, offset,

-:4231: WARNING:DEEP_INDENTATION: Too many leading tabs - consider code refactoring
#4231: FILE: drivers/gpu/drm/i915/selftests/i915_gem_gtt.c:444:
+						if (offset + vma->node.size > hole_end)

-:4247: WARNING:DEEP_INDENTATION: Too many leading tabs - consider code refactoring
#4247: FILE: drivers/gpu/drm/i915/selftests/i915_gem_gtt.c:453:
+						if (offset < hole_start + vma->node.size)

-:4259: WARNING:DEEP_INDENTATION: Too many leading tabs - consider code refactoring
#4259: FILE: drivers/gpu/drm/i915/selftests/i915_gem_gtt.c:476:
+						if (offset + vma->node.size > hole_end)

-:4275: WARNING:DEEP_INDENTATION: Too many leading tabs - consider code refactoring
#4275: FILE: drivers/gpu/drm/i915/selftests/i915_gem_gtt.c:485:
+						if (offset < hole_start + vma->node.size)

-:4287: WARNING:DEEP_INDENTATION: Too many leading tabs - consider code refactoring
#4287: FILE: drivers/gpu/drm/i915/selftests/i915_gem_gtt.c:509:
+						if (offset + vma->size >= hole_end)

-:4303: WARNING:DEEP_INDENTATION: Too many leading tabs - consider code refactoring
#4303: FILE: drivers/gpu/drm/i915/selftests/i915_gem_gtt.c:518:
+						if (offset < hole_start + vma->size)

-:4315: WARNING:DEEP_INDENTATION: Too many leading tabs - consider code refactoring
#4315: FILE: drivers/gpu/drm/i915/selftests/i915_gem_gtt.c:541:
+						if (offset + vma->size >= hole_end)

total: 0 errors, 9 warnings, 2 checks, 4801 lines checked

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

* [Intel-gfx] ✗ Fi.CI.SPARSE: warning for series starting with [01/20] drm/i915: Preallocate stashes for vma page-directories
  2020-07-06  6:19 [Intel-gfx] s/obj->mm.lock// Chris Wilson
                   ` (20 preceding siblings ...)
  2020-07-06  6:28 ` [Intel-gfx] ✗ Fi.CI.CHECKPATCH: warning for series starting with [01/20] drm/i915: Preallocate stashes for vma page-directories Patchwork
@ 2020-07-06  6:29 ` Patchwork
  2020-07-06  6:51 ` [Intel-gfx] ✓ Fi.CI.BAT: success " Patchwork
                   ` (2 subsequent siblings)
  24 siblings, 0 replies; 65+ messages in thread
From: Patchwork @ 2020-07-06  6:29 UTC (permalink / raw)
  To: Chris Wilson; +Cc: intel-gfx

== Series Details ==

Series: series starting with [01/20] drm/i915: Preallocate stashes for vma page-directories
URL   : https://patchwork.freedesktop.org/series/79129/
State : warning

== Summary ==

$ dim sparse --fast origin/drm-tip
Sparse version: v0.6.0
Fast mode used, each commit won't be checked separately.
+drivers/gpu/drm/i915/selftests/i915_syncmap.c:80:54: warning: dubious: x | !y

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

* [Intel-gfx] ✓ Fi.CI.BAT: success for series starting with [01/20] drm/i915: Preallocate stashes for vma page-directories
  2020-07-06  6:19 [Intel-gfx] s/obj->mm.lock// Chris Wilson
                   ` (21 preceding siblings ...)
  2020-07-06  6:29 ` [Intel-gfx] ✗ Fi.CI.SPARSE: " Patchwork
@ 2020-07-06  6:51 ` Patchwork
  2020-07-06  7:55 ` [Intel-gfx] ✗ Fi.CI.IGT: failure " Patchwork
  2020-07-27 18:53 ` [Intel-gfx] s/obj->mm.lock// Thomas Hellström (Intel)
  24 siblings, 0 replies; 65+ messages in thread
From: Patchwork @ 2020-07-06  6:51 UTC (permalink / raw)
  To: Chris Wilson; +Cc: intel-gfx

== Series Details ==

Series: series starting with [01/20] drm/i915: Preallocate stashes for vma page-directories
URL   : https://patchwork.freedesktop.org/series/79129/
State : success

== Summary ==

CI Bug Log - changes from CI_DRM_8708 -> Patchwork_18082
====================================================

Summary
-------

  **SUCCESS**

  No regressions found.

  External URL: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/index.html

Known issues
------------

  Here are the changes found in Patchwork_18082 that come from known issues:

### IGT changes ###

#### Issues hit ####

  * igt@gem_exec_suspend@basic-s0:
    - fi-ilk-650:         [PASS][1] -> [DMESG-WARN][2] ([i915#164])
   [1]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/fi-ilk-650/igt@gem_exec_suspend@basic-s0.html
   [2]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/fi-ilk-650/igt@gem_exec_suspend@basic-s0.html

  * igt@gem_flink_basic@double-flink:
    - fi-tgl-y:           [PASS][3] -> [DMESG-WARN][4] ([i915#402]) +1 similar issue
   [3]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/fi-tgl-y/igt@gem_flink_basic@double-flink.html
   [4]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/fi-tgl-y/igt@gem_flink_basic@double-flink.html

  * igt@kms_busy@basic@flip:
    - fi-kbl-x1275:       [PASS][5] -> [DMESG-WARN][6] ([i915#62] / [i915#92] / [i915#95])
   [5]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/fi-kbl-x1275/igt@kms_busy@basic@flip.html
   [6]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/fi-kbl-x1275/igt@kms_busy@basic@flip.html

  
#### Possible fixes ####

  * igt@gem_exec_suspend@basic-s0:
    - fi-tgl-u2:          [FAIL][7] ([i915#1888]) -> [PASS][8]
   [7]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/fi-tgl-u2/igt@gem_exec_suspend@basic-s0.html
   [8]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/fi-tgl-u2/igt@gem_exec_suspend@basic-s0.html

  * igt@i915_pm_backlight@basic-brightness:
    - fi-whl-u:           [DMESG-WARN][9] ([i915#95]) -> [PASS][10]
   [9]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/fi-whl-u/igt@i915_pm_backlight@basic-brightness.html
   [10]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/fi-whl-u/igt@i915_pm_backlight@basic-brightness.html

  * igt@vgem_basic@setversion:
    - fi-tgl-y:           [DMESG-WARN][11] ([i915#402]) -> [PASS][12] +1 similar issue
   [11]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/fi-tgl-y/igt@vgem_basic@setversion.html
   [12]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/fi-tgl-y/igt@vgem_basic@setversion.html

  
#### Warnings ####

  * igt@gem_exec_suspend@basic-s0:
    - fi-kbl-x1275:       [DMESG-WARN][13] ([i915#62] / [i915#92]) -> [DMESG-WARN][14] ([i915#62] / [i915#92] / [i915#95]) +3 similar issues
   [13]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/fi-kbl-x1275/igt@gem_exec_suspend@basic-s0.html
   [14]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/fi-kbl-x1275/igt@gem_exec_suspend@basic-s0.html

  * igt@kms_cursor_legacy@basic-flip-after-cursor-varying-size:
    - fi-kbl-x1275:       [DMESG-WARN][15] ([i915#62] / [i915#92] / [i915#95]) -> [DMESG-WARN][16] ([i915#62] / [i915#92]) +2 similar issues
   [15]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/fi-kbl-x1275/igt@kms_cursor_legacy@basic-flip-after-cursor-varying-size.html
   [16]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/fi-kbl-x1275/igt@kms_cursor_legacy@basic-flip-after-cursor-varying-size.html

  
  [i915#164]: https://gitlab.freedesktop.org/drm/intel/issues/164
  [i915#1888]: https://gitlab.freedesktop.org/drm/intel/issues/1888
  [i915#402]: https://gitlab.freedesktop.org/drm/intel/issues/402
  [i915#62]: https://gitlab.freedesktop.org/drm/intel/issues/62
  [i915#92]: https://gitlab.freedesktop.org/drm/intel/issues/92
  [i915#95]: https://gitlab.freedesktop.org/drm/intel/issues/95


Participating hosts (43 -> 37)
------------------------------

  Additional (1): fi-skl-guc 
  Missing    (7): fi-ilk-m540 fi-hsw-4200u fi-byt-squawks fi-bsw-cyan fi-ctg-p8600 fi-byt-clapper fi-bdw-samus 


Build changes
-------------

  * Linux: CI_DRM_8708 -> Patchwork_18082

  CI-20190529: 20190529
  CI_DRM_8708: 170e94a1430fd0a4f0841ad0f7366904d52e49be @ git://anongit.freedesktop.org/gfx-ci/linux
  IGT_5722: 9985cf23e9db9557bc7d714f5b72602e427497d3 @ git://anongit.freedesktop.org/xorg/app/intel-gpu-tools
  Patchwork_18082: 33bcfafbc4ab5abd3e686c0e7b58aa7f7b61da4a @ git://anongit.freedesktop.org/gfx-ci/linux


== Linux commits ==

33bcfafbc4ab drm/i915: Track i915_vma with its own reference counter
f3622825131d drm/i915/gem: Replace i915_gem_object.mm.mutex with reservation_ww_class
0c763806ba4f drm/i915/gem: Pull execbuf dma resv under a single critical section
2036c3ef6f3e drm/i915: Add an implementation for i915_gem_ww_ctx locking, v2.
3a96506f63d3 drm/i915/gem: Reintroduce multiple passes for reloc processing
8b519af3185f drm/i915/gem: Include secure batch in common execbuf pinning
6002974eb6ce drm/i915/gem: Include cmdparser in common execbuf pinning
92aa37aeda01 drm/i915/gem: Bind the fence async for execbuf
a1fb11db685d drm/i915/gem: Asynchronous GTT unbinding
8669de2e382e drm/i915/gem: Separate the ww_mutex walker into its own list
fe694efd1728 drm/i915: Export a preallocate variant of i915_active_acquire()
03e637530b30 drm/i915/gem: Assign context id for async work
ed90e5840625 drm/i915: Always defer fenced work to the worker
acb85ac92a2f drm/i915: Add list_for_each_entry_safe_continue_reverse
c3cc3ce10691 drm/i915/gem: Remove the call for no-evict i915_vma_pin
6779fcaed114 drm/i915/gem: Break apart the early i915_vma_pin from execbuf object lookup
a8aa1e74f47e drm/i915/gem: Rename execbuf.bind_link to unbound_link
1da308be5b55 drm/i915/gem: Don't drop the timeline lock during execbuf
5ff5ae1fb079 drm/i915: Switch to object allocations for page directories
b5c5d0ce6d3d drm/i915: Preallocate stashes for vma page-directories

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/index.html
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

* [Intel-gfx] ✗ Fi.CI.IGT: failure for series starting with [01/20] drm/i915: Preallocate stashes for vma page-directories
  2020-07-06  6:19 [Intel-gfx] s/obj->mm.lock// Chris Wilson
                   ` (22 preceding siblings ...)
  2020-07-06  6:51 ` [Intel-gfx] ✓ Fi.CI.BAT: success " Patchwork
@ 2020-07-06  7:55 ` Patchwork
  2020-07-27 18:53 ` [Intel-gfx] s/obj->mm.lock// Thomas Hellström (Intel)
  24 siblings, 0 replies; 65+ messages in thread
From: Patchwork @ 2020-07-06  7:55 UTC (permalink / raw)
  To: Chris Wilson; +Cc: intel-gfx

== Series Details ==

Series: series starting with [01/20] drm/i915: Preallocate stashes for vma page-directories
URL   : https://patchwork.freedesktop.org/series/79129/
State : failure

== Summary ==

CI Bug Log - changes from CI_DRM_8708_full -> Patchwork_18082_full
====================================================

Summary
-------

  **FAILURE**

  Serious unknown changes coming with Patchwork_18082_full absolutely need to be
  verified manually.
  
  If you think the reported changes have nothing to do with the changes
  introduced in Patchwork_18082_full, please notify your bug team to allow them
  to document this new failure mode, which will reduce false positives in CI.

  

Possible new issues
-------------------

  Here are the unknown changes that may have been introduced in Patchwork_18082_full:

### IGT changes ###

#### Possible regressions ####

  * igt@gem_close@many-handles-one-vma:
    - shard-glk:          [PASS][1] -> [FAIL][2]
   [1]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-glk5/igt@gem_close@many-handles-one-vma.html
   [2]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-glk7/igt@gem_close@many-handles-one-vma.html
    - shard-apl:          [PASS][3] -> [FAIL][4]
   [3]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-apl1/igt@gem_close@many-handles-one-vma.html
   [4]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-apl7/igt@gem_close@many-handles-one-vma.html
    - shard-tglb:         [PASS][5] -> [FAIL][6]
   [5]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-tglb8/igt@gem_close@many-handles-one-vma.html
   [6]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-tglb1/igt@gem_close@many-handles-one-vma.html
    - shard-kbl:          [PASS][7] -> [FAIL][8]
   [7]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-kbl6/igt@gem_close@many-handles-one-vma.html
   [8]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-kbl2/igt@gem_close@many-handles-one-vma.html
    - shard-hsw:          [PASS][9] -> [FAIL][10]
   [9]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-hsw2/igt@gem_close@many-handles-one-vma.html
   [10]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-hsw7/igt@gem_close@many-handles-one-vma.html
    - shard-snb:          [PASS][11] -> [FAIL][12]
   [11]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-snb6/igt@gem_close@many-handles-one-vma.html
   [12]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-snb6/igt@gem_close@many-handles-one-vma.html
    - shard-iclb:         [PASS][13] -> [FAIL][14]
   [13]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-iclb1/igt@gem_close@many-handles-one-vma.html
   [14]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-iclb4/igt@gem_close@many-handles-one-vma.html

  * igt@gem_ctx_ringsize@active@bcs0:
    - shard-skl:          NOTRUN -> [FAIL][15]
   [15]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-skl4/igt@gem_ctx_ringsize@active@bcs0.html

  * igt@gem_exec_schedule@pi-shared-iova@bcs0:
    - shard-skl:          [PASS][16] -> [DMESG-WARN][17]
   [16]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-skl3/igt@gem_exec_schedule@pi-shared-iova@bcs0.html
   [17]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-skl5/igt@gem_exec_schedule@pi-shared-iova@bcs0.html
    - shard-glk:          [PASS][18] -> [DMESG-WARN][19] +1 similar issue
   [18]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-glk2/igt@gem_exec_schedule@pi-shared-iova@bcs0.html
   [19]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-glk1/igt@gem_exec_schedule@pi-shared-iova@bcs0.html
    - shard-apl:          [PASS][20] -> [DMESG-WARN][21]
   [20]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-apl8/igt@gem_exec_schedule@pi-shared-iova@bcs0.html
   [21]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-apl8/igt@gem_exec_schedule@pi-shared-iova@bcs0.html

  * igt@gem_exec_schedule@pi-shared-iova@rcs0:
    - shard-kbl:          [PASS][22] -> [DMESG-WARN][23]
   [22]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-kbl6/igt@gem_exec_schedule@pi-shared-iova@rcs0.html
   [23]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-kbl3/igt@gem_exec_schedule@pi-shared-iova@rcs0.html

  * igt@gem_exec_schedule@pi-shared-iova@vcs1:
    - shard-tglb:         [PASS][24] -> [DMESG-WARN][25]
   [24]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-tglb1/igt@gem_exec_schedule@pi-shared-iova@vcs1.html
   [25]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-tglb5/igt@gem_exec_schedule@pi-shared-iova@vcs1.html

  * igt@gem_exec_schedule@pi-shared-iova@vecs0:
    - shard-iclb:         [PASS][26] -> [DMESG-WARN][27]
   [26]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-iclb1/igt@gem_exec_schedule@pi-shared-iova@vecs0.html
   [27]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-iclb1/igt@gem_exec_schedule@pi-shared-iova@vecs0.html

  * igt@gem_sync@basic-store-each:
    - shard-skl:          [PASS][28] -> [FAIL][29] +1 similar issue
   [28]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-skl4/igt@gem_sync@basic-store-each.html
   [29]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-skl4/igt@gem_sync@basic-store-each.html

  * igt@kms_psr@sprite_plane_onoff:
    - shard-skl:          NOTRUN -> [DMESG-WARN][30]
   [30]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-skl9/igt@kms_psr@sprite_plane_onoff.html

  
New tests
---------

  New tests have been introduced between CI_DRM_8708_full and Patchwork_18082_full:

### New IGT tests (1) ###

  * igt@i915_selftest@mock@acquire:
    - Statuses : 2 pass(s)
    - Exec time: [0.86, 1.76] s

  

Known issues
------------

  Here are the changes found in Patchwork_18082_full that come from known issues:

### IGT changes ###

#### Issues hit ####

  * igt@gem_ctx_persistence@replace@bcs0:
    - shard-skl:          [PASS][31] -> [FAIL][32] ([i915#2092])
   [31]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-skl5/igt@gem_ctx_persistence@replace@bcs0.html
   [32]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-skl10/igt@gem_ctx_persistence@replace@bcs0.html

  * igt@gem_ringfill@basic-all:
    - shard-glk:          [PASS][33] -> [DMESG-WARN][34] ([i915#118] / [i915#95])
   [33]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-glk8/igt@gem_ringfill@basic-all.html
   [34]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-glk8/igt@gem_ringfill@basic-all.html

  * igt@gem_unfence_active_buffers:
    - shard-kbl:          [PASS][35] -> [DMESG-WARN][36] ([i915#93] / [i915#95]) +3 similar issues
   [35]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-kbl1/igt@gem_unfence_active_buffers.html
   [36]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-kbl1/igt@gem_unfence_active_buffers.html

  * igt@gem_userptr_blits@readonly-unsync:
    - shard-skl:          [PASS][37] -> [TIMEOUT][38] ([i915#1958] / [i915#2119]) +2 similar issues
   [37]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-skl8/igt@gem_userptr_blits@readonly-unsync.html
   [38]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-skl8/igt@gem_userptr_blits@readonly-unsync.html

  * igt@gen9_exec_parse@bb-start-out:
    - shard-apl:          [PASS][39] -> [DMESG-WARN][40] ([i915#1635] / [i915#95]) +11 similar issues
   [39]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-apl8/igt@gen9_exec_parse@bb-start-out.html
   [40]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-apl4/igt@gen9_exec_parse@bb-start-out.html

  * igt@kms_big_fb@linear-32bpp-rotate-180:
    - shard-skl:          [PASS][41] -> [DMESG-WARN][42] ([i915#1982]) +18 similar issues
   [41]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-skl4/igt@kms_big_fb@linear-32bpp-rotate-180.html
   [42]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-skl1/igt@kms_big_fb@linear-32bpp-rotate-180.html

  * igt@kms_flip@2x-flip-vs-expired-vblank@bc-hdmi-a1-hdmi-a2:
    - shard-glk:          [PASS][43] -> [FAIL][44] ([i915#79])
   [43]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-glk4/igt@kms_flip@2x-flip-vs-expired-vblank@bc-hdmi-a1-hdmi-a2.html
   [44]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-glk7/igt@kms_flip@2x-flip-vs-expired-vblank@bc-hdmi-a1-hdmi-a2.html

  * igt@kms_flip@flip-vs-suspend@a-edp1:
    - shard-skl:          [PASS][45] -> [INCOMPLETE][46] ([i915#198])
   [45]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-skl6/igt@kms_flip@flip-vs-suspend@a-edp1.html
   [46]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-skl3/igt@kms_flip@flip-vs-suspend@a-edp1.html

  * igt@kms_flip@flip-vs-suspend@b-dp1:
    - shard-kbl:          [PASS][47] -> [DMESG-WARN][48] ([i915#180])
   [47]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-kbl1/igt@kms_flip@flip-vs-suspend@b-dp1.html
   [48]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-kbl1/igt@kms_flip@flip-vs-suspend@b-dp1.html

  * igt@kms_flip@plain-flip-ts-check-interruptible@b-hdmi-a1:
    - shard-glk:          [PASS][49] -> [FAIL][50] ([i915#1928])
   [49]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-glk9/igt@kms_flip@plain-flip-ts-check-interruptible@b-hdmi-a1.html
   [50]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-glk4/igt@kms_flip@plain-flip-ts-check-interruptible@b-hdmi-a1.html

  * igt@kms_frontbuffer_tracking@fbc-badstride:
    - shard-tglb:         [PASS][51] -> [DMESG-WARN][52] ([i915#1982]) +1 similar issue
   [51]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-tglb3/igt@kms_frontbuffer_tracking@fbc-badstride.html
   [52]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-tglb3/igt@kms_frontbuffer_tracking@fbc-badstride.html

  * igt@kms_pipe_crc_basic@suspend-read-crc-pipe-b:
    - shard-iclb:         [PASS][53] -> [INCOMPLETE][54] ([CI#80] / [i915#1185])
   [53]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-iclb6/igt@kms_pipe_crc_basic@suspend-read-crc-pipe-b.html
   [54]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-iclb3/igt@kms_pipe_crc_basic@suspend-read-crc-pipe-b.html

  * igt@kms_vblank@crtc-id:
    - shard-kbl:          [PASS][55] -> [DMESG-WARN][56] ([i915#1982])
   [55]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-kbl4/igt@kms_vblank@crtc-id.html
   [56]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-kbl3/igt@kms_vblank@crtc-id.html

  * igt@perf@blocking-parameterized:
    - shard-iclb:         [PASS][57] -> [FAIL][58] ([i915#1542])
   [57]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-iclb2/igt@perf@blocking-parameterized.html
   [58]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-iclb7/igt@perf@blocking-parameterized.html

  * igt@perf_pmu@semaphore-busy@rcs0:
    - shard-glk:          [PASS][59] -> [FAIL][60] ([i915#1820])
   [59]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-glk8/igt@perf_pmu@semaphore-busy@rcs0.html
   [60]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-glk8/igt@perf_pmu@semaphore-busy@rcs0.html

  * igt@perf_pmu@semaphore-busy@vcs0:
    - shard-kbl:          [PASS][61] -> [FAIL][62] ([i915#1820]) +2 similar issues
   [61]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-kbl2/igt@perf_pmu@semaphore-busy@vcs0.html
   [62]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-kbl3/igt@perf_pmu@semaphore-busy@vcs0.html

  
#### Possible fixes ####

  * igt@gem_ctx_persistence@legacy-engines-mixed-process@blt:
    - shard-skl:          [FAIL][63] ([i915#1528]) -> [PASS][64]
   [63]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-skl5/igt@gem_ctx_persistence@legacy-engines-mixed-process@blt.html
   [64]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-skl10/igt@gem_ctx_persistence@legacy-engines-mixed-process@blt.html

  * igt@gem_exec_fence@parallel@vcs0:
    - shard-glk:          [DMESG-WARN][65] ([i915#118] / [i915#95]) -> [PASS][66] +1 similar issue
   [65]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-glk4/igt@gem_exec_fence@parallel@vcs0.html
   [66]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-glk6/igt@gem_exec_fence@parallel@vcs0.html

  * igt@gem_exec_reloc@basic-concurrent0:
    - shard-tglb:         [FAIL][67] ([i915#1930]) -> [PASS][68]
   [67]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-tglb3/igt@gem_exec_reloc@basic-concurrent0.html
   [68]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-tglb3/igt@gem_exec_reloc@basic-concurrent0.html
    - shard-apl:          [FAIL][69] ([i915#1930]) -> [PASS][70]
   [69]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-apl8/igt@gem_exec_reloc@basic-concurrent0.html
   [70]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-apl4/igt@gem_exec_reloc@basic-concurrent0.html
    - shard-kbl:          [FAIL][71] ([i915#1930]) -> [PASS][72]
   [71]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-kbl2/igt@gem_exec_reloc@basic-concurrent0.html
   [72]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-kbl3/igt@gem_exec_reloc@basic-concurrent0.html
    - shard-hsw:          [FAIL][73] ([i915#1930]) -> [PASS][74]
   [73]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-hsw5/igt@gem_exec_reloc@basic-concurrent0.html
   [74]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-hsw4/igt@gem_exec_reloc@basic-concurrent0.html
    - shard-iclb:         [FAIL][75] ([i915#1930]) -> [PASS][76]
   [75]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-iclb2/igt@gem_exec_reloc@basic-concurrent0.html
   [76]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-iclb7/igt@gem_exec_reloc@basic-concurrent0.html
    - shard-skl:          [FAIL][77] ([i915#1930]) -> [PASS][78]
   [77]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-skl4/igt@gem_exec_reloc@basic-concurrent0.html
   [78]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-skl4/igt@gem_exec_reloc@basic-concurrent0.html

  * igt@gem_exec_reloc@basic-concurrent16:
    - shard-snb:          [FAIL][79] ([i915#1930]) -> [PASS][80] +1 similar issue
   [79]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-snb2/igt@gem_exec_reloc@basic-concurrent16.html
   [80]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-snb4/igt@gem_exec_reloc@basic-concurrent16.html
    - shard-iclb:         [INCOMPLETE][81] ([i915#1958]) -> [PASS][82]
   [81]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-iclb7/igt@gem_exec_reloc@basic-concurrent16.html
   [82]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-iclb8/igt@gem_exec_reloc@basic-concurrent16.html
    - shard-skl:          [INCOMPLETE][83] ([i915#1958]) -> [PASS][84]
   [83]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-skl10/igt@gem_exec_reloc@basic-concurrent16.html
   [84]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-skl9/igt@gem_exec_reloc@basic-concurrent16.html
    - shard-kbl:          [INCOMPLETE][85] ([i915#1958]) -> [PASS][86]
   [85]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-kbl6/igt@gem_exec_reloc@basic-concurrent16.html
   [86]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-kbl2/igt@gem_exec_reloc@basic-concurrent16.html
    - shard-apl:          [INCOMPLETE][87] ([i915#1635] / [i915#1958]) -> [PASS][88]
   [87]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-apl4/igt@gem_exec_reloc@basic-concurrent16.html
   [88]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-apl2/igt@gem_exec_reloc@basic-concurrent16.html
    - shard-tglb:         [INCOMPLETE][89] ([i915#1958]) -> [PASS][90]
   [89]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-tglb7/igt@gem_exec_reloc@basic-concurrent16.html
   [90]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-tglb5/igt@gem_exec_reloc@basic-concurrent16.html
    - shard-glk:          [INCOMPLETE][91] ([i915#1958] / [i915#58] / [k.org#198133]) -> [PASS][92]
   [91]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-glk3/igt@gem_exec_reloc@basic-concurrent16.html
   [92]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-glk3/igt@gem_exec_reloc@basic-concurrent16.html

  * igt@gem_mmap_gtt@cpuset-big-copy:
    - shard-skl:          [DMESG-WARN][93] ([i915#1982]) -> [PASS][94] +4 similar issues
   [93]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-skl7/igt@gem_mmap_gtt@cpuset-big-copy.html
   [94]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-skl2/igt@gem_mmap_gtt@cpuset-big-copy.html

  * igt@gem_tiled_partial_pwrite_pread@reads:
    - shard-hsw:          [TIMEOUT][95] ([i915#1958] / [i915#2119]) -> [PASS][96] +3 similar issues
   [95]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-hsw1/igt@gem_tiled_partial_pwrite_pread@reads.html
   [96]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-hsw1/igt@gem_tiled_partial_pwrite_pread@reads.html

  * igt@gem_userptr_blits@unsync-unmap-after-close:
    - shard-kbl:          [DMESG-WARN][97] ([i915#93] / [i915#95]) -> [PASS][98]
   [97]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-kbl1/igt@gem_userptr_blits@unsync-unmap-after-close.html
   [98]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-kbl1/igt@gem_userptr_blits@unsync-unmap-after-close.html

  * igt@i915_selftest@mock@requests:
    - shard-skl:          [INCOMPLETE][99] ([i915#198] / [i915#2110]) -> [PASS][100]
   [99]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-skl2/igt@i915_selftest@mock@requests.html
   [100]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-skl9/igt@i915_selftest@mock@requests.html

  * igt@kms_big_fb@x-tiled-64bpp-rotate-180:
    - shard-glk:          [DMESG-FAIL][101] ([i915#118] / [i915#95]) -> [PASS][102]
   [101]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-glk8/igt@kms_big_fb@x-tiled-64bpp-rotate-180.html
   [102]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-glk5/igt@kms_big_fb@x-tiled-64bpp-rotate-180.html

  * igt@kms_cursor_crc@pipe-c-cursor-128x128-rapid-movement:
    - shard-tglb:         [INCOMPLETE][103] ([i915#750]) -> [PASS][104]
   [103]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-tglb8/igt@kms_cursor_crc@pipe-c-cursor-128x128-rapid-movement.html
   [104]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-tglb1/igt@kms_cursor_crc@pipe-c-cursor-128x128-rapid-movement.html

  * igt@kms_cursor_crc@pipe-c-cursor-256x85-onscreen:
    - shard-skl:          [FAIL][105] ([i915#54]) -> [PASS][106]
   [105]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-skl6/igt@kms_cursor_crc@pipe-c-cursor-256x85-onscreen.html
   [106]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-skl3/igt@kms_cursor_crc@pipe-c-cursor-256x85-onscreen.html

  * igt@kms_draw_crc@draw-method-xrgb8888-mmap-wc-ytiled:
    - shard-apl:          [DMESG-WARN][107] ([i915#1635] / [i915#95]) -> [PASS][108] +9 similar issues
   [107]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-apl7/igt@kms_draw_crc@draw-method-xrgb8888-mmap-wc-ytiled.html
   [108]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-apl2/igt@kms_draw_crc@draw-method-xrgb8888-mmap-wc-ytiled.html

  * igt@kms_fbcon_fbt@psr-suspend:
    - shard-iclb:         [INCOMPLETE][109] ([i915#1185]) -> [PASS][110]
   [109]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-iclb2/igt@kms_fbcon_fbt@psr-suspend.html
   [110]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-iclb4/igt@kms_fbcon_fbt@psr-suspend.html

  * igt@kms_flip@flip-vs-expired-vblank-interruptible@c-dp1:
    - shard-apl:          [FAIL][111] ([i915#79]) -> [PASS][112]
   [111]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-apl7/igt@kms_flip@flip-vs-expired-vblank-interruptible@c-dp1.html
   [112]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-apl2/igt@kms_flip@flip-vs-expired-vblank-interruptible@c-dp1.html

  * igt@kms_flip@flip-vs-suspend-interruptible@a-dp1:
    - shard-kbl:          [DMESG-WARN][113] ([i915#180]) -> [PASS][114] +5 similar issues
   [113]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-kbl6/igt@kms_flip@flip-vs-suspend-interruptible@a-dp1.html
   [114]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-kbl2/igt@kms_flip@flip-vs-suspend-interruptible@a-dp1.html

  * igt@kms_flip@flip-vs-suspend@a-edp1:
    - shard-tglb:         [INCOMPLETE][115] ([i915#456]) -> [PASS][116]
   [115]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-tglb2/igt@kms_flip@flip-vs-suspend@a-edp1.html
   [116]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-tglb2/igt@kms_flip@flip-vs-suspend@a-edp1.html

  * igt@kms_frontbuffer_tracking@fbcpsr-1p-primscrn-shrfb-plflip-blt:
    - shard-tglb:         [DMESG-WARN][117] ([i915#1982]) -> [PASS][118]
   [117]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-tglb6/igt@kms_frontbuffer_tracking@fbcpsr-1p-primscrn-shrfb-plflip-blt.html
   [118]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-tglb1/igt@kms_frontbuffer_tracking@fbcpsr-1p-primscrn-shrfb-plflip-blt.html

  * igt@kms_hdr@bpc-switch-suspend:
    - shard-skl:          [FAIL][119] ([i915#1188]) -> [PASS][120]
   [119]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-skl2/igt@kms_hdr@bpc-switch-suspend.html
   [120]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-skl9/igt@kms_hdr@bpc-switch-suspend.html

  * igt@kms_pipe_crc_basic@hang-read-crc-pipe-b:
    - shard-skl:          [FAIL][121] ([i915#53]) -> [PASS][122]
   [121]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-skl4/igt@kms_pipe_crc_basic@hang-read-crc-pipe-b.html
   [122]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-skl4/igt@kms_pipe_crc_basic@hang-read-crc-pipe-b.html

  * igt@kms_plane_alpha_blend@pipe-c-constant-alpha-min:
    - shard-skl:          [FAIL][123] ([fdo#108145] / [i915#265]) -> [PASS][124]
   [123]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-skl6/igt@kms_plane_alpha_blend@pipe-c-constant-alpha-min.html
   [124]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-skl3/igt@kms_plane_alpha_blend@pipe-c-constant-alpha-min.html

  
#### Warnings ####

  * igt@i915_pm_dc@dc3co-vpb-simulation:
    - shard-iclb:         [SKIP][125] ([i915#588]) -> [SKIP][126] ([i915#658])
   [125]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-iclb2/igt@i915_pm_dc@dc3co-vpb-simulation.html
   [126]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-iclb7/igt@i915_pm_dc@dc3co-vpb-simulation.html

  * igt@i915_pm_rc6_residency@rc6-idle:
    - shard-iclb:         [WARN][127] ([i915#1515]) -> [FAIL][128] ([i915#1515])
   [127]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-iclb3/igt@i915_pm_rc6_residency@rc6-idle.html
   [128]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-iclb1/igt@i915_pm_rc6_residency@rc6-idle.html

  * igt@kms_big_fb@x-tiled-32bpp-rotate-90:
    - shard-apl:          [SKIP][129] ([fdo#109271]) -> [SKIP][130] ([fdo#109271] / [i915#1635]) +3 similar issues
   [129]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-apl2/igt@kms_big_fb@x-tiled-32bpp-rotate-90.html
   [130]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-apl1/igt@kms_big_fb@x-tiled-32bpp-rotate-90.html

  * igt@kms_color_chamelium@pipe-invalid-degamma-lut-sizes:
    - shard-apl:          [SKIP][131] ([fdo#109271] / [fdo#111827]) -> [SKIP][132] ([fdo#109271] / [fdo#111827] / [i915#1635]) +2 similar issues
   [131]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-apl2/igt@kms_color_chamelium@pipe-invalid-degamma-lut-sizes.html
   [132]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-apl4/igt@kms_color_chamelium@pipe-invalid-degamma-lut-sizes.html

  * igt@kms_content_protection@atomic:
    - shard-kbl:          [DMESG-FAIL][133] ([fdo#110321] / [i915#95]) -> [TIMEOUT][134] ([i915#1319] / [i915#2119])
   [133]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-kbl2/igt@kms_content_protection@atomic.html
   [134]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-kbl7/igt@kms_content_protection@atomic.html

  * igt@kms_flip@flip-vs-expired-vblank@a-edp1:
    - shard-skl:          [FAIL][135] ([i915#46]) -> [DMESG-WARN][136] ([i915#1982])
   [135]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-skl6/igt@kms_flip@flip-vs-expired-vblank@a-edp1.html
   [136]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-skl4/igt@kms_flip@flip-vs-expired-vblank@a-edp1.html

  * igt@kms_frontbuffer_tracking@fbcpsr-1p-primscrn-spr-indfb-draw-mmap-wc:
    - shard-hsw:          [INCOMPLETE][137] ([CI#80]) -> [SKIP][138] ([fdo#109271])
   [137]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-hsw1/igt@kms_frontbuffer_tracking@fbcpsr-1p-primscrn-spr-indfb-draw-mmap-wc.html
   [138]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-hsw1/igt@kms_frontbuffer_tracking@fbcpsr-1p-primscrn-spr-indfb-draw-mmap-wc.html

  * igt@kms_frontbuffer_tracking@psr-2p-scndscrn-shrfb-pgflip-blt:
    - shard-hsw:          [TIMEOUT][139] ([i915#1958] / [i915#2119]) -> [SKIP][140] ([fdo#109271])
   [139]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-hsw1/igt@kms_frontbuffer_tracking@psr-2p-scndscrn-shrfb-pgflip-blt.html
   [140]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-hsw1/igt@kms_frontbuffer_tracking@psr-2p-scndscrn-shrfb-pgflip-blt.html

  * igt@kms_plane@plane-panning-top-left-pipe-d-planes:
    - shard-apl:          [SKIP][141] ([fdo#109271] / [i915#1635]) -> [SKIP][142] ([fdo#109271]) +2 similar issues
   [141]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-apl2/igt@kms_plane@plane-panning-top-left-pipe-d-planes.html
   [142]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-apl1/igt@kms_plane@plane-panning-top-left-pipe-d-planes.html

  * igt@runner@aborted:
    - shard-apl:          ([FAIL][143], [FAIL][144]) ([i915#1610] / [i915#1635] / [i915#2110] / [i915#637]) -> ([FAIL][145], [FAIL][146]) ([i915#1610] / [i915#1635] / [i915#2110])
   [143]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-apl2/igt@runner@aborted.html
   [144]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-apl3/igt@runner@aborted.html
   [145]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-apl8/igt@runner@aborted.html
   [146]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-apl2/igt@runner@aborted.html
    - shard-skl:          ([FAIL][147], [FAIL][148]) ([i915#2110] / [i915#69]) -> ([FAIL][149], [FAIL][150]) ([i915#69])
   [147]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-skl8/igt@runner@aborted.html
   [148]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_8708/shard-skl2/igt@runner@aborted.html
   [149]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-skl5/igt@runner@aborted.html
   [150]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/shard-skl9/igt@runner@aborted.html

  
  [CI#80]: https://gitlab.freedesktop.org/gfx-ci/i915-infra/issues/80
  [fdo#108145]: https://bugs.freedesktop.org/show_bug.cgi?id=108145
  [fdo#109271]: https://bugs.freedesktop.org/show_bug.cgi?id=109271
  [fdo#110321]: https://bugs.freedesktop.org/show_bug.cgi?id=110321
  [fdo#111827]: https://bugs.freedesktop.org/show_bug.cgi?id=111827
  [i915#118]: https://gitlab.freedesktop.org/drm/intel/issues/118
  [i915#1185]: https://gitlab.freedesktop.org/drm/intel/issues/1185
  [i915#1188]: https://gitlab.freedesktop.org/drm/intel/issues/1188
  [i915#1319]: https://gitlab.freedesktop.org/drm/intel/issues/1319
  [i915#1515]: https://gitlab.freedesktop.org/drm/intel/issues/1515
  [i915#1528]: https://gitlab.freedesktop.org/drm/intel/issues/1528
  [i915#1542]: https://gitlab.freedesktop.org/drm/intel/issues/1542
  [i915#1610]: https://gitlab.freedesktop.org/drm/intel/issues/1610
  [i915#1635]: https://gitlab.freedesktop.org/drm/intel/issues/1635
  [i915#180]: https://gitlab.freedesktop.org/drm/intel/issues/180
  [i915#1820]: https://gitlab.freedesktop.org/drm/intel/issues/1820
  [i915#1928]: https://gitlab.freedesktop.org/drm/intel/issues/1928
  [i915#1930]: https://gitlab.freedesktop.org/drm/intel/issues/1930
  [i915#1958]: https://gitlab.freedesktop.org/drm/intel/issues/1958
  [i915#198]: https://gitlab.freedesktop.org/drm/intel/issues/198
  [i915#1982]: https://gitlab.freedesktop.org/drm/intel/issues/1982
  [i915#2092]: https://gitlab.freedesktop.org/drm/intel/issues/2092
  [i915#2110]: https://gitlab.freedesktop.org/drm/intel/issues/2110
  [i915#2119]: https://gitlab.freedesktop.org/drm/intel/issues/2119
  [i915#265]: https://gitlab.freedesktop.org/drm/intel/issues/265
  [i915#456]: https://gitlab.freedesktop.org/drm/intel/issues/456
  [i915#46]: https://gitlab.freedesktop.org/drm/intel/issues/46
  [i915#53]: https://gitlab.freedesktop.org/drm/intel/issues/53
  [i915#54]: https://gitlab.freedesktop.org/drm/intel/issues/54
  [i915#58]: https://gitlab.freedesktop.org/drm/intel/issues/58
  [i915#588]: https://gitlab.freedesktop.org/drm/intel/issues/588
  [i915#637]: https://gitlab.freedesktop.org/drm/intel/issues/637
  [i915#658]: https://gitlab.freedesktop.org/drm/intel/issues/658
  [i915#69]: https://gitlab.freedesktop.org/drm/intel/issues/69
  [i915#750]: https://gitlab.freedesktop.org/drm/intel/issues/750
  [i915#79]: https://gitlab.freedesktop.org/drm/intel/issues/79
  [i915#93]: https://gitlab.freedesktop.org/drm/intel/issues/93
  [i915#95]: https://gitlab.freedesktop.org/drm/intel/issues/95
  [k.org#198133]: https://bugzilla.kernel.org/show_bug.cgi?id=198133


Partici

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_18082/index.html
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Intel-gfx] [PATCH 17/20] drm/i915: Add an implementation for i915_gem_ww_ctx locking, v2.
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 17/20] drm/i915: Add an implementation for i915_gem_ww_ctx locking, v2 Chris Wilson
@ 2020-07-06 17:21   ` kernel test robot
  0 siblings, 0 replies; 65+ messages in thread
From: kernel test robot @ 2020-07-06 17:21 UTC (permalink / raw)
  To: kbuild-all

[-- Attachment #1: Type: text/plain, Size: 2187 bytes --]

Hi Chris,

I love your patch! Yet something to improve:

[auto build test ERROR on drm-intel/for-linux-next]
[also build test ERROR on drm-tip/drm-tip next-20200706]
[cannot apply to v5.8-rc4]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use  as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Chris-Wilson/drm-i915-Preallocate-stashes-for-vma-page-directories/20200706-152007
base:   git://anongit.freedesktop.org/drm-intel for-linux-next
config: x86_64-randconfig-a013-20200706 (attached as .config)
compiler: clang version 11.0.0 (https://github.com/llvm/llvm-project a378c0449507e00e96534ff9ce9034e185425182)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # install x86_64 cross compiling tool for clang build
        # apt-get install binutils-x86-64-linux-gnu
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=x86_64 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   In file included from <built-in>:3:
>> drivers/gpu/drm/i915/mm/i915_acquire_ctx.h:6:9: error: '__I915_ACQIURE_CTX_H__' is used as a header guard here, followed by #define of a different macro [-Werror,-Wheader-guard]
   #ifndef __I915_ACQIURE_CTX_H__
           ^~~~~~~~~~~~~~~~~~~~~~
   drivers/gpu/drm/i915/mm/i915_acquire_ctx.h:7:9: note: '__I915_ACQUIRE_CTX_H__' is defined here; did you mean '__I915_ACQIURE_CTX_H__'?
   #define __I915_ACQUIRE_CTX_H__
           ^~~~~~~~~~~~~~~~~~~~~~
           __I915_ACQIURE_CTX_H__
   1 error generated.

vim +/__I915_ACQIURE_CTX_H__ +6 drivers/gpu/drm/i915/mm/i915_acquire_ctx.h

   > 6	#ifndef __I915_ACQIURE_CTX_H__
     7	#define __I915_ACQUIRE_CTX_H__
     8	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org

[-- Attachment #2: config.gz --]
[-- Type: application/gzip, Size: 31384 bytes --]

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Intel-gfx] [PATCH 01/20] drm/i915: Preallocate stashes for vma page-directories
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 01/20] drm/i915: Preallocate stashes for vma page-directories Chris Wilson
@ 2020-07-06 18:15   ` Matthew Auld
  2020-07-06 18:20     ` Chris Wilson
  0 siblings, 1 reply; 65+ messages in thread
From: Matthew Auld @ 2020-07-06 18:15 UTC (permalink / raw)
  To: Chris Wilson; +Cc: Intel Graphics Development

On Mon, 6 Jul 2020 at 07:19, Chris Wilson <chris@chris-wilson.co.uk> wrote:
>
> We need to make the DMA allocations used for page directories to be
> performed up front so that we can include those allocations in our
> memory reservation pass. The downside is that we have to assume the
> worst case, even before we know the final layout, and always allocate
> enough page directories for this object, even when there will be overlap.
>
> It should be noted that the lifetime for the page directories DMA is
> more or less decoupled from individual fences as they will be shared
> across objects across timelines.
>
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>

<snip>

> @@ -874,11 +873,21 @@ int i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
>                 return err;
>
>         if (flags & vma->vm->bind_async_flags) {
> +               u64 max_size;
> +
>                 work = i915_vma_work();
>                 if (!work) {
>                         err = -ENOMEM;
>                         goto err_pages;
>                 }
> +
> +               work->vm = i915_vm_get(vma->vm);
> +
> +               /* Allocate enough page directories to cover worst case */
> +               max_size = max(size, vma->size);
> +               if (flags & PIN_MAPPABLE)
> +                       max_size = max_t(u64, max_size, vma->fence_size);
> +               i915_vm_alloc_pt_stash(vma->vm, &work->stash, max_size);

Why do we need to take into account size when allocating the page
directories, and not just vma->size/fence_size, since the difference
is only really padding? It could be gigantic thanks to pad_to_size.
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Intel-gfx] [PATCH 01/20] drm/i915: Preallocate stashes for vma page-directories
  2020-07-06 18:15   ` Matthew Auld
@ 2020-07-06 18:20     ` Chris Wilson
  0 siblings, 0 replies; 65+ messages in thread
From: Chris Wilson @ 2020-07-06 18:20 UTC (permalink / raw)
  To: Matthew Auld; +Cc: Intel Graphics Development

Quoting Matthew Auld (2020-07-06 19:15:27)
> On Mon, 6 Jul 2020 at 07:19, Chris Wilson <chris@chris-wilson.co.uk> wrote:
> >
> > We need to make the DMA allocations used for page directories to be
> > performed up front so that we can include those allocations in our
> > memory reservation pass. The downside is that we have to assume the
> > worst case, even before we know the final layout, and always allocate
> > enough page directories for this object, even when there will be overlap.
> >
> > It should be noted that the lifetime for the page directories DMA is
> > more or less decoupled from individual fences as they will be shared
> > across objects across timelines.
> >
> > Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> 
> <snip>
> 
> > @@ -874,11 +873,21 @@ int i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
> >                 return err;
> >
> >         if (flags & vma->vm->bind_async_flags) {
> > +               u64 max_size;
> > +
> >                 work = i915_vma_work();
> >                 if (!work) {
> >                         err = -ENOMEM;
> >                         goto err_pages;
> >                 }
> > +
> > +               work->vm = i915_vm_get(vma->vm);
> > +
> > +               /* Allocate enough page directories to cover worst case */
> > +               max_size = max(size, vma->size);
> > +               if (flags & PIN_MAPPABLE)
> > +                       max_size = max_t(u64, max_size, vma->fence_size);
> > +               i915_vm_alloc_pt_stash(vma->vm, &work->stash, max_size);
> 
> Why do we need to take into account size when allocating the page
> directories, and not just vma->size/fence_size, since the difference
> is only really padding? It could be gigantic thanks to pad_to_size.

We need to cover the PT entries we actually insert, which is indeed
vma->size. So yes, this is overkill, I was trying to match the size of
the node we create, but as you say we don't populate all of that node
and leave the unused portion/tail pointing to scratch.

That gets rid of some clumsy code!
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Intel-gfx] [PATCH 02/20] drm/i915: Switch to object allocations for page directories
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 02/20] drm/i915: Switch to object allocations for page directories Chris Wilson
@ 2020-07-06 19:06   ` Matthew Auld
  2020-07-06 19:31     ` Chris Wilson
  2020-07-06 20:01     ` Chris Wilson
  0 siblings, 2 replies; 65+ messages in thread
From: Matthew Auld @ 2020-07-06 19:06 UTC (permalink / raw)
  To: Chris Wilson; +Cc: Intel Graphics Development

On Mon, 6 Jul 2020 at 07:19, Chris Wilson <chris@chris-wilson.co.uk> wrote:
>
> The GEM object is grossly overweight for the practicality of tracking
> large numbers of individual pages, yet it is currently our only
> abstraction for tracking DMA allocations. Since those allocations need
> to be reserved upfront before an operation, and that we need to break
> away from simple system memory, we need to ditch using plain struct page
> wrappers.
>
> In the process, we drop the WC mapping as we ended up clflushing
> everything anyway due to various issues across a wider range of
> platforms. Though in a future step, we need to drop the kmap_atomic
> approach which suggests we need to pre-map all the pages and keep them
> mapped.
>
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>

<snip>

>
> -int setup_scratch_page(struct i915_address_space *vm, gfp_t gfp)
> +int setup_scratch_page(struct i915_address_space *vm)
>  {
>         unsigned long size;
>
> @@ -338,21 +174,22 @@ int setup_scratch_page(struct i915_address_space *vm, gfp_t gfp)
>          */
>         size = I915_GTT_PAGE_SIZE_4K;
>         if (i915_vm_is_4lvl(vm) &&
> -           HAS_PAGE_SIZES(vm->i915, I915_GTT_PAGE_SIZE_64K)) {
> +           HAS_PAGE_SIZES(vm->i915, I915_GTT_PAGE_SIZE_64K))
>                 size = I915_GTT_PAGE_SIZE_64K;
> -               gfp |= __GFP_NOWARN;
> -       }
> -       gfp |= __GFP_ZERO | __GFP_RETRY_MAYFAIL;
>
>         do {
> -               unsigned int order = get_order(size);
> -               struct page *page;
> -               dma_addr_t addr;
> +               struct drm_i915_gem_object *obj;
>
> -               page = alloc_pages(gfp, order);
> -               if (unlikely(!page))
> +               obj = vm->alloc_pt_dma(vm, size);
> +               if (IS_ERR(obj))
>                         goto skip;
>
> +               if (pin_pt_dma(vm, obj))
> +                       goto skip_obj;
> +
> +               if (obj->mm.page_sizes.sg < size)
> +                       goto skip_obj;
> +

We should still check the alignment of the final dma address
somewhere, in the case of 64K. I have for sure seen dma misalignment
here before.

>                 /*
>                  * Use a non-zero scratch page for debugging.
>                  *
> @@ -362,61 +199,28 @@ int setup_scratch_page(struct i915_address_space *vm, gfp_t gfp)
>                  * should it ever be accidentally used, the effect should be
>                  * fairly benign.
>                  */
> -               poison_scratch_page(page, size);
> -
> -               addr = dma_map_page_attrs(vm->dma,
> -                                         page, 0, size,
> -                                         PCI_DMA_BIDIRECTIONAL,
> -                                         DMA_ATTR_SKIP_CPU_SYNC |
> -                                         DMA_ATTR_NO_WARN);
> -               if (unlikely(dma_mapping_error(vm->dma, addr)))
> -                       goto free_page;
> -
> -               if (unlikely(!IS_ALIGNED(addr, size)))
> -                       goto unmap_page;
> -
> -               vm->scratch[0].base.page = page;
> -               vm->scratch[0].base.daddr = addr;
> -               vm->scratch_order = order;
> +               poison_scratch_page(obj);

Since this is now an internal object, which lacks proper clearing, do
we need to nuke the page(s) somewhere, since it is visible to
userspace? The posion_scratch seems to only be for debug builds.

> +
> +               vm->scratch[0] = obj;
> +               vm->scratch_order = get_order(size);
>                 return 0;
>
> -unmap_page:
> -               dma_unmap_page(vm->dma, addr, size, PCI_DMA_BIDIRECTIONAL);
> -free_page:
> -               __free_pages(page, order);
> +skip_obj:
> +               i915_gem_object_put(obj);
>  skip:
>                 if (size == I915_GTT_PAGE_SIZE_4K)
>                         return -ENOMEM;
>
>                 size = I915_GTT_PAGE_SIZE_4K;
> -               gfp &= ~__GFP_NOWARN;
>         } while (1);
>  }
>
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Intel-gfx] [PATCH 02/20] drm/i915: Switch to object allocations for page directories
  2020-07-06 19:06   ` Matthew Auld
@ 2020-07-06 19:31     ` Chris Wilson
  2020-07-06 20:01     ` Chris Wilson
  1 sibling, 0 replies; 65+ messages in thread
From: Chris Wilson @ 2020-07-06 19:31 UTC (permalink / raw)
  To: Matthew Auld; +Cc: Intel Graphics Development

Quoting Matthew Auld (2020-07-06 20:06:38)
> On Mon, 6 Jul 2020 at 07:19, Chris Wilson <chris@chris-wilson.co.uk> wrote:
> >
> > The GEM object is grossly overweight for the practicality of tracking
> > large numbers of individual pages, yet it is currently our only
> > abstraction for tracking DMA allocations. Since those allocations need
> > to be reserved upfront before an operation, and that we need to break
> > away from simple system memory, we need to ditch using plain struct page
> > wrappers.
> >
> > In the process, we drop the WC mapping as we ended up clflushing
> > everything anyway due to various issues across a wider range of
> > platforms. Though in a future step, we need to drop the kmap_atomic
> > approach which suggests we need to pre-map all the pages and keep them
> > mapped.
> >
> > Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> 
> <snip>
> 
> >
> > -int setup_scratch_page(struct i915_address_space *vm, gfp_t gfp)
> > +int setup_scratch_page(struct i915_address_space *vm)
> >  {
> >         unsigned long size;
> >
> > @@ -338,21 +174,22 @@ int setup_scratch_page(struct i915_address_space *vm, gfp_t gfp)
> >          */
> >         size = I915_GTT_PAGE_SIZE_4K;
> >         if (i915_vm_is_4lvl(vm) &&
> > -           HAS_PAGE_SIZES(vm->i915, I915_GTT_PAGE_SIZE_64K)) {
> > +           HAS_PAGE_SIZES(vm->i915, I915_GTT_PAGE_SIZE_64K))
> >                 size = I915_GTT_PAGE_SIZE_64K;
> > -               gfp |= __GFP_NOWARN;
> > -       }
> > -       gfp |= __GFP_ZERO | __GFP_RETRY_MAYFAIL;
> >
> >         do {
> > -               unsigned int order = get_order(size);
> > -               struct page *page;
> > -               dma_addr_t addr;
> > +               struct drm_i915_gem_object *obj;
> >
> > -               page = alloc_pages(gfp, order);
> > -               if (unlikely(!page))
> > +               obj = vm->alloc_pt_dma(vm, size);
> > +               if (IS_ERR(obj))
> >                         goto skip;
> >
> > +               if (pin_pt_dma(vm, obj))
> > +                       goto skip_obj;
> > +
> > +               if (obj->mm.page_sizes.sg < size)
> > +                       goto skip_obj;
> > +
> 
> We should still check the alignment of the final dma address
> somewhere, in the case of 64K. I have for sure seen dma misalignment
> here before.

True. A nuisance to reject a 64K contiguous page because of dma
misalignment. I wonder if we can pass alignment to iommu.

/*
 * DMA_ATTR_NO_KERNEL_MAPPING: Lets the platform to avoid creating a kernel
 * virtual mapping for the allocated buffer.
 */
#define DMA_ATTR_NO_KERNEL_MAPPING      (1UL << 4)

Ahem. I hope that isn't being applied to all of our buffers....

/*
 * DMA_ATTR_WRITE_COMBINE: Specifies that writes to the mapping may be
 * buffered to improve performance.
 */
#define DMA_ATTR_WRITE_COMBINE          (1UL << 2)

On the other hand; tell me more dma-mapping.h!

But nothing there to influence alignment :(

> > @@ -362,61 +199,28 @@ int setup_scratch_page(struct i915_address_space *vm, gfp_t gfp)
> >                  * should it ever be accidentally used, the effect should be
> >                  * fairly benign.
> >                  */
> > -               poison_scratch_page(page, size);
> > -
> > -               addr = dma_map_page_attrs(vm->dma,
> > -                                         page, 0, size,
> > -                                         PCI_DMA_BIDIRECTIONAL,
> > -                                         DMA_ATTR_SKIP_CPU_SYNC |
> > -                                         DMA_ATTR_NO_WARN);
> > -               if (unlikely(dma_mapping_error(vm->dma, addr)))
> > -                       goto free_page;
> > -
> > -               if (unlikely(!IS_ALIGNED(addr, size)))
> > -                       goto unmap_page;
> > -
> > -               vm->scratch[0].base.page = page;
> > -               vm->scratch[0].base.daddr = addr;
> > -               vm->scratch_order = order;
> > +               poison_scratch_page(obj);
> 
> Since this is now an internal object, which lacks proper clearing, do
> we need to nuke the page(s) somewhere, since it is visible to
> userspace? The posion_scratch seems to only be for debug builds.

Yes. It needs to be cleared [when not poisoned].
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Intel-gfx] [PATCH 02/20] drm/i915: Switch to object allocations for page directories
  2020-07-06 19:06   ` Matthew Auld
  2020-07-06 19:31     ` Chris Wilson
@ 2020-07-06 20:01     ` Chris Wilson
  2020-07-06 21:08       ` Chris Wilson
  1 sibling, 1 reply; 65+ messages in thread
From: Chris Wilson @ 2020-07-06 20:01 UTC (permalink / raw)
  To: Matthew Auld; +Cc: Intel Graphics Development

Quoting Matthew Auld (2020-07-06 20:06:38)
> On Mon, 6 Jul 2020 at 07:19, Chris Wilson <chris@chris-wilson.co.uk> wrote:
> >
> > The GEM object is grossly overweight for the practicality of tracking
> > large numbers of individual pages, yet it is currently our only
> > abstraction for tracking DMA allocations. Since those allocations need
> > to be reserved upfront before an operation, and that we need to break
> > away from simple system memory, we need to ditch using plain struct page
> > wrappers.
> >
> > In the process, we drop the WC mapping as we ended up clflushing
> > everything anyway due to various issues across a wider range of
> > platforms. Though in a future step, we need to drop the kmap_atomic
> > approach which suggests we need to pre-map all the pages and keep them
> > mapped.
> >
> > Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> 
> <snip>

The other thing I'm toying with is whether to keep the unused preallocs
around on the ppgtt. The cost of the conservative allocations is
decidedly high in CI [thanks to memdebug tracking and verifying], as
each PD is itself a 4KiB table of pointers (as well as the 4KiB dma
page). Outside of CI, the issue is not as pressing, and if a workload
does not reach steady state quickly, then the extra allocations are just
one of many worries. For the steady state, we benefit from not having
surplus pages trapped in the ppgtt, as that is the danger of the
caching, when should we trim it?

[Previously we only allocated on demand, but keep a *small* number of WC
pages around because converting a page to/from WC was expensive.]

If there's a good answer for when we can/should free the surplus cache,
it's probably worth pursuing. Or if we deem it worth to keep the cache
limited to 15 entries [reusing a pagevec].

Overallocation is pita for having to preallocate; since we basically
have to have at least 2 PD for each level + actual span. For every vma,
even when bundling the insertions, as we don't know which entries will
be used until much later. So we almost certainly overallocate 4 PD
[16KiB system + 16KiB dma] for every single vma. Even a 15 entry stash
will be quickly exhausted; oh well.
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Intel-gfx] [PATCH 02/20] drm/i915: Switch to object allocations for page directories
  2020-07-06 20:01     ` Chris Wilson
@ 2020-07-06 21:08       ` Chris Wilson
  0 siblings, 0 replies; 65+ messages in thread
From: Chris Wilson @ 2020-07-06 21:08 UTC (permalink / raw)
  To: Matthew Auld; +Cc: Intel Graphics Development

Quoting Chris Wilson (2020-07-06 21:01:54)
> Overallocation is pita for having to preallocate; since we basically
> have to have at least 2 PD for each level + actual span. For every vma,
> even when bundling the insertions, as we don't know which entries will
> be used until much later. So we almost certainly overallocate 4 PD
> [16KiB system + 16KiB dma] for every single vma. Even a 15 entry stash
> will be quickly exhausted; oh well.

Just a note to self, a pvec[15] is too quickly exhausted and does not
hide the allocation costs.
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Intel-gfx] [PATCH 08/20] drm/i915: Always defer fenced work to the worker
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 08/20] drm/i915: Always defer fenced work to the worker Chris Wilson
@ 2020-07-08 12:18   ` Tvrtko Ursulin
  2020-07-08 12:25     ` Chris Wilson
  0 siblings, 1 reply; 65+ messages in thread
From: Tvrtko Ursulin @ 2020-07-08 12:18 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 06/07/2020 07:19, Chris Wilson wrote:
> Currently, if an error is raised we always call the cleanup locally
> [and skip the main work callback]. However, some future users may need
> to take a mutex to cleanup and so we cannot immediately execute the
> cleanup as we may still be in interrupt context.
> 
> With the execute-immediate flag, for most cases this should result in
> immediate cleanup of an error.
> 
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> ---
>   drivers/gpu/drm/i915/i915_sw_fence_work.c | 25 +++++++++++------------
>   1 file changed, 12 insertions(+), 13 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/i915_sw_fence_work.c b/drivers/gpu/drm/i915/i915_sw_fence_work.c
> index a3a81bb8f2c3..29f63ebc24e8 100644
> --- a/drivers/gpu/drm/i915/i915_sw_fence_work.c
> +++ b/drivers/gpu/drm/i915/i915_sw_fence_work.c
> @@ -16,11 +16,14 @@ static void fence_complete(struct dma_fence_work *f)
>   static void fence_work(struct work_struct *work)
>   {
>   	struct dma_fence_work *f = container_of(work, typeof(*f), work);
> -	int err;
>   
> -	err = f->ops->work(f);
> -	if (err)
> -		dma_fence_set_error(&f->dma, err);
> +	if (!f->dma.error) {
> +		int err;
> +
> +		err = f->ops->work(f);
> +		if (err)
> +			dma_fence_set_error(&f->dma, err);
> +	}
>   
>   	fence_complete(f);
>   	dma_fence_put(&f->dma);
> @@ -36,15 +39,11 @@ fence_notify(struct i915_sw_fence *fence, enum i915_sw_fence_notify state)
>   		if (fence->error)
>   			dma_fence_set_error(&f->dma, fence->error);
>   
> -		if (!f->dma.error) {
> -			dma_fence_get(&f->dma);
> -			if (test_bit(DMA_FENCE_WORK_IMM, &f->dma.flags))
> -				fence_work(&f->work);
> -			else
> -				queue_work(system_unbound_wq, &f->work);
> -		} else {
> -			fence_complete(f);
> -		}
> +		dma_fence_get(&f->dma);
> +		if (test_bit(DMA_FENCE_WORK_IMM, &f->dma.flags))
> +			fence_work(&f->work);
> +		else
> +			queue_work(system_unbound_wq, &f->work);

Right the commit wording really confused me since it is obviously still 
deferring stuff to the worker. By "fenced work" I understand you 
actually mean more like "never signal non-immediate work from the notify 
callback" (even in the error case).

Regards,

Tvrtko

>   		break;
>   
>   	case FENCE_FREE:
> 
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Intel-gfx] [PATCH 08/20] drm/i915: Always defer fenced work to the worker
  2020-07-08 12:18   ` Tvrtko Ursulin
@ 2020-07-08 12:25     ` Chris Wilson
  0 siblings, 0 replies; 65+ messages in thread
From: Chris Wilson @ 2020-07-08 12:25 UTC (permalink / raw)
  To: Tvrtko Ursulin, intel-gfx

Quoting Tvrtko Ursulin (2020-07-08 13:18:21)
> 
> On 06/07/2020 07:19, Chris Wilson wrote:
> > Currently, if an error is raised we always call the cleanup locally
> > [and skip the main work callback]. However, some future users may need
> > to take a mutex to cleanup and so we cannot immediately execute the
> > cleanup as we may still be in interrupt context.
> > 
> > With the execute-immediate flag, for most cases this should result in
> > immediate cleanup of an error.
> > 
> > Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> > ---
> >   drivers/gpu/drm/i915/i915_sw_fence_work.c | 25 +++++++++++------------
> >   1 file changed, 12 insertions(+), 13 deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/i915/i915_sw_fence_work.c b/drivers/gpu/drm/i915/i915_sw_fence_work.c
> > index a3a81bb8f2c3..29f63ebc24e8 100644
> > --- a/drivers/gpu/drm/i915/i915_sw_fence_work.c
> > +++ b/drivers/gpu/drm/i915/i915_sw_fence_work.c
> > @@ -16,11 +16,14 @@ static void fence_complete(struct dma_fence_work *f)
> >   static void fence_work(struct work_struct *work)
> >   {
> >       struct dma_fence_work *f = container_of(work, typeof(*f), work);
> > -     int err;
> >   
> > -     err = f->ops->work(f);
> > -     if (err)
> > -             dma_fence_set_error(&f->dma, err);
> > +     if (!f->dma.error) {
> > +             int err;
> > +
> > +             err = f->ops->work(f);
> > +             if (err)
> > +                     dma_fence_set_error(&f->dma, err);
> > +     }
> >   
> >       fence_complete(f);
> >       dma_fence_put(&f->dma);
> > @@ -36,15 +39,11 @@ fence_notify(struct i915_sw_fence *fence, enum i915_sw_fence_notify state)
> >               if (fence->error)
> >                       dma_fence_set_error(&f->dma, fence->error);
> >   
> > -             if (!f->dma.error) {
> > -                     dma_fence_get(&f->dma);
> > -                     if (test_bit(DMA_FENCE_WORK_IMM, &f->dma.flags))
> > -                             fence_work(&f->work);
> > -                     else
> > -                             queue_work(system_unbound_wq, &f->work);
> > -             } else {
> > -                     fence_complete(f);
> > -             }
> > +             dma_fence_get(&f->dma);
> > +             if (test_bit(DMA_FENCE_WORK_IMM, &f->dma.flags))
> > +                     fence_work(&f->work);
> > +             else
> > +                     queue_work(system_unbound_wq, &f->work);
> 
> Right the commit wording really confused me since it is obviously still 
> deferring stuff to the worker. By "fenced work" I understand you 
> actually mean more like "never signal non-immediate work from the notify 
> callback" (even in the error case).

Work that had to wait for a fence should always take the worker to avoid
being run in interrupt context (from the fence signal callback), even in
the case of errors [so that the work can take its carefully considered
mutexes]. I anticipate that most errors will be generated before we
starting waiting for fences, and those will remain immediately executed
(when asked to do so by the caller).
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Intel-gfx] [PATCH 09/20] drm/i915/gem: Assign context id for async work
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 09/20] drm/i915/gem: Assign context id for async work Chris Wilson
@ 2020-07-08 12:26   ` Tvrtko Ursulin
  2020-07-08 12:42     ` Chris Wilson
  2020-07-08 12:45     ` Tvrtko Ursulin
  0 siblings, 2 replies; 65+ messages in thread
From: Tvrtko Ursulin @ 2020-07-08 12:26 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 06/07/2020 07:19, Chris Wilson wrote:
> Allocate a few dma fence context id that we can use to associate async work
> [for the CPU] launched on behalf of this context. For extra fun, we allow
> a configurable concurrency width.
> 
> A current example would be that we spawn an unbound worker for every
> userptr get_pages. In the future, we wish to charge this work to the
> context that initiated the async work and to impose concurrency limits
> based on the context.
> 
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> ---
>   drivers/gpu/drm/i915/gem/i915_gem_context.c       | 4 ++++
>   drivers/gpu/drm/i915/gem/i915_gem_context.h       | 6 ++++++
>   drivers/gpu/drm/i915/gem/i915_gem_context_types.h | 6 ++++++
>   3 files changed, 16 insertions(+)
> 
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context.c b/drivers/gpu/drm/i915/gem/i915_gem_context.c
> index 41784df51e58..bd68746327b3 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_context.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_context.c
> @@ -714,6 +714,10 @@ __create_context(struct drm_i915_private *i915)
>   	ctx->sched.priority = I915_USER_PRIORITY(I915_PRIORITY_NORMAL);
>   	mutex_init(&ctx->mutex);
>   
> +	ctx->async.width = rounddown_pow_of_two(num_online_cpus());
> +	ctx->async.context = dma_fence_context_alloc(ctx->async.width);
> +	ctx->async.width--;

Hey I had a tri-core CPU back in the day.. :) Really, I can only assume 
you are oding some tricks with masks which maybe only work with power of 
2 num cpus? Hard to say.. please explain in a comment.

I don't even understand what the context will be for yet and why it 
needs a separate context id.

> +
>   	spin_lock_init(&ctx->stale.lock);
>   	INIT_LIST_HEAD(&ctx->stale.engines);
>   
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context.h b/drivers/gpu/drm/i915/gem/i915_gem_context.h
> index 3702b2fb27ab..e104ff0ae740 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_context.h
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_context.h
> @@ -134,6 +134,12 @@ int i915_gem_context_setparam_ioctl(struct drm_device *dev, void *data,
>   int i915_gem_context_reset_stats_ioctl(struct drm_device *dev, void *data,
>   				       struct drm_file *file);
>   
> +static inline u64 i915_gem_context_async_id(struct i915_gem_context *ctx)
> +{
> +	return (ctx->async.context +
> +		(atomic_fetch_inc(&ctx->async.cur) & ctx->async.width));
> +}
> +
>   static inline struct i915_gem_context *
>   i915_gem_context_get(struct i915_gem_context *ctx)
>   {
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context_types.h b/drivers/gpu/drm/i915/gem/i915_gem_context_types.h
> index ae14ca24a11f..52561f98000f 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_context_types.h
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_context_types.h
> @@ -85,6 +85,12 @@ struct i915_gem_context {
>   
>   	struct intel_timeline *timeline;
>   
> +	struct {
> +		u64 context;
> +		atomic_t cur;

What is cur? In which patch it gets used? (Can't see it.)

> +		unsigned int width;
> +	} async;
> +
>   	/**
>   	 * @vm: unique address space (GTT)
>   	 *
> 

Regards,

Tvrtko
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Intel-gfx] [PATCH 09/20] drm/i915/gem: Assign context id for async work
  2020-07-08 12:26   ` Tvrtko Ursulin
@ 2020-07-08 12:42     ` Chris Wilson
  2020-07-08 14:24       ` Tvrtko Ursulin
  2020-07-08 12:45     ` Tvrtko Ursulin
  1 sibling, 1 reply; 65+ messages in thread
From: Chris Wilson @ 2020-07-08 12:42 UTC (permalink / raw)
  To: Tvrtko Ursulin, intel-gfx

Quoting Tvrtko Ursulin (2020-07-08 13:26:24)
> 
> On 06/07/2020 07:19, Chris Wilson wrote:
> > Allocate a few dma fence context id that we can use to associate async work
> > [for the CPU] launched on behalf of this context. For extra fun, we allow
> > a configurable concurrency width.
> > 
> > A current example would be that we spawn an unbound worker for every
> > userptr get_pages. In the future, we wish to charge this work to the
> > context that initiated the async work and to impose concurrency limits
> > based on the context.
> > 
> > Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> > ---
> >   drivers/gpu/drm/i915/gem/i915_gem_context.c       | 4 ++++
> >   drivers/gpu/drm/i915/gem/i915_gem_context.h       | 6 ++++++
> >   drivers/gpu/drm/i915/gem/i915_gem_context_types.h | 6 ++++++
> >   3 files changed, 16 insertions(+)
> > 
> > diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context.c b/drivers/gpu/drm/i915/gem/i915_gem_context.c
> > index 41784df51e58..bd68746327b3 100644
> > --- a/drivers/gpu/drm/i915/gem/i915_gem_context.c
> > +++ b/drivers/gpu/drm/i915/gem/i915_gem_context.c
> > @@ -714,6 +714,10 @@ __create_context(struct drm_i915_private *i915)
> >       ctx->sched.priority = I915_USER_PRIORITY(I915_PRIORITY_NORMAL);
> >       mutex_init(&ctx->mutex);
> >   
> > +     ctx->async.width = rounddown_pow_of_two(num_online_cpus());
> > +     ctx->async.context = dma_fence_context_alloc(ctx->async.width);
> > +     ctx->async.width--;
> 
> Hey I had a tri-core CPU back in the day.. :) Really, I can only assume 
> you are oding some tricks with masks which maybe only work with power of 
> 2 num cpus? Hard to say.. please explain in a comment.

Just a pot mask, that fits in the currently available set of CPUs.
 
> I don't even understand what the context will be for yet and why it 
> needs a separate context id.

The longer term view is that I want to pull the various async tasks we
use into a CPU scheduling kthread[s], that shares the same priority
inheritance of tasks. The issue at the moment is that as we use the
system_wq, that imposes an implicit FIFO ordering on our tasks upsetting
our context priorities. This is a step towards that to start looking at
how we might limit concurrency in various stages by using a bunch of
timelines for each stage, and queuing our work along each timeline before
submitting to an unbound system_wq. [The immediate goal is to limit how
much of the CPU one client can hog by submitting deferred work that would
run in parallel, with a view to making that configurable per-context.]

> >       spin_lock_init(&ctx->stale.lock);
> >       INIT_LIST_HEAD(&ctx->stale.engines);
> >   
> > diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context.h b/drivers/gpu/drm/i915/gem/i915_gem_context.h
> > index 3702b2fb27ab..e104ff0ae740 100644
> > --- a/drivers/gpu/drm/i915/gem/i915_gem_context.h
> > +++ b/drivers/gpu/drm/i915/gem/i915_gem_context.h
> > @@ -134,6 +134,12 @@ int i915_gem_context_setparam_ioctl(struct drm_device *dev, void *data,
> >   int i915_gem_context_reset_stats_ioctl(struct drm_device *dev, void *data,
> >                                      struct drm_file *file);
> >   
> > +static inline u64 i915_gem_context_async_id(struct i915_gem_context *ctx)
> > +{
> > +     return (ctx->async.context +
> > +             (atomic_fetch_inc(&ctx->async.cur) & ctx->async.width));
> > +}
> > +
> >   static inline struct i915_gem_context *
> >   i915_gem_context_get(struct i915_gem_context *ctx)
> >   {
> > diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context_types.h b/drivers/gpu/drm/i915/gem/i915_gem_context_types.h
> > index ae14ca24a11f..52561f98000f 100644
> > --- a/drivers/gpu/drm/i915/gem/i915_gem_context_types.h
> > +++ b/drivers/gpu/drm/i915/gem/i915_gem_context_types.h
> > @@ -85,6 +85,12 @@ struct i915_gem_context {
> >   
> >       struct intel_timeline *timeline;
> >   
> > +     struct {
> > +             u64 context;
> > +             atomic_t cur;
> 
> What is cur? In which patch it gets used? (Can't see it.)

See i915_gem_context_async_id() above.
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Intel-gfx] [PATCH 09/20] drm/i915/gem: Assign context id for async work
  2020-07-08 12:26   ` Tvrtko Ursulin
  2020-07-08 12:42     ` Chris Wilson
@ 2020-07-08 12:45     ` Tvrtko Ursulin
  1 sibling, 0 replies; 65+ messages in thread
From: Tvrtko Ursulin @ 2020-07-08 12:45 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 08/07/2020 13:26, Tvrtko Ursulin wrote:
> 
> On 06/07/2020 07:19, Chris Wilson wrote:
>> Allocate a few dma fence context id that we can use to associate async 
>> work
>> [for the CPU] launched on behalf of this context. For extra fun, we allow
>> a configurable concurrency width.
>>
>> A current example would be that we spawn an unbound worker for every
>> userptr get_pages. In the future, we wish to charge this work to the
>> context that initiated the async work and to impose concurrency limits
>> based on the context.
>>
>> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
>> ---
>>   drivers/gpu/drm/i915/gem/i915_gem_context.c       | 4 ++++
>>   drivers/gpu/drm/i915/gem/i915_gem_context.h       | 6 ++++++
>>   drivers/gpu/drm/i915/gem/i915_gem_context_types.h | 6 ++++++
>>   3 files changed, 16 insertions(+)
>>
>> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context.c 
>> b/drivers/gpu/drm/i915/gem/i915_gem_context.c
>> index 41784df51e58..bd68746327b3 100644
>> --- a/drivers/gpu/drm/i915/gem/i915_gem_context.c
>> +++ b/drivers/gpu/drm/i915/gem/i915_gem_context.c
>> @@ -714,6 +714,10 @@ __create_context(struct drm_i915_private *i915)
>>       ctx->sched.priority = I915_USER_PRIORITY(I915_PRIORITY_NORMAL);
>>       mutex_init(&ctx->mutex);
>> +    ctx->async.width = rounddown_pow_of_two(num_online_cpus());
>> +    ctx->async.context = dma_fence_context_alloc(ctx->async.width);
>> +    ctx->async.width--;
> 
> Hey I had a tri-core CPU back in the day.. :) Really, I can only assume 
> you are oding some tricks with masks which maybe only work with power of 
> 2 num cpus? Hard to say.. please explain in a comment.

Doh missed rounddown_pow_of_two..

> I don't even understand what the context will be for yet and why it 
> needs a separate context id.
> 
>> +
>>       spin_lock_init(&ctx->stale.lock);
>>       INIT_LIST_HEAD(&ctx->stale.engines);
>> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context.h 
>> b/drivers/gpu/drm/i915/gem/i915_gem_context.h
>> index 3702b2fb27ab..e104ff0ae740 100644
>> --- a/drivers/gpu/drm/i915/gem/i915_gem_context.h
>> +++ b/drivers/gpu/drm/i915/gem/i915_gem_context.h
>> @@ -134,6 +134,12 @@ int i915_gem_context_setparam_ioctl(struct 
>> drm_device *dev, void *data,
>>   int i915_gem_context_reset_stats_ioctl(struct drm_device *dev, void 
>> *data,
>>                          struct drm_file *file);
>> +static inline u64 i915_gem_context_async_id(struct i915_gem_context 
>> *ctx)
>> +{
>> +    return (ctx->async.context +
>> +        (atomic_fetch_inc(&ctx->async.cur) & ctx->async.width));
>> +}
>> +
>>   static inline struct i915_gem_context *
>>   i915_gem_context_get(struct i915_gem_context *ctx)
>>   {
>> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context_types.h 
>> b/drivers/gpu/drm/i915/gem/i915_gem_context_types.h
>> index ae14ca24a11f..52561f98000f 100644
>> --- a/drivers/gpu/drm/i915/gem/i915_gem_context_types.h
>> +++ b/drivers/gpu/drm/i915/gem/i915_gem_context_types.h
>> @@ -85,6 +85,12 @@ struct i915_gem_context {
>>       struct intel_timeline *timeline;
>> +    struct {
>> +        u64 context;
>> +        atomic_t cur;
> 
> What is cur? In which patch it gets used? (Can't see it.)

Found it. Just more explanation why it is needed is required.

Regards,

Tvrtko

> 
>> +        unsigned int width;
>> +    } async;
>> +
>>       /**
>>        * @vm: unique address space (GTT)
>>        *
>>
> 
> Regards,
> 
> Tvrtko
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Intel-gfx] [PATCH 09/20] drm/i915/gem: Assign context id for async work
  2020-07-08 12:42     ` Chris Wilson
@ 2020-07-08 14:24       ` Tvrtko Ursulin
  2020-07-08 15:36         ` Chris Wilson
  0 siblings, 1 reply; 65+ messages in thread
From: Tvrtko Ursulin @ 2020-07-08 14:24 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 08/07/2020 13:42, Chris Wilson wrote:
> Quoting Tvrtko Ursulin (2020-07-08 13:26:24)
>>
>> On 06/07/2020 07:19, Chris Wilson wrote:
>>> Allocate a few dma fence context id that we can use to associate async work
>>> [for the CPU] launched on behalf of this context. For extra fun, we allow
>>> a configurable concurrency width.
>>>
>>> A current example would be that we spawn an unbound worker for every
>>> userptr get_pages. In the future, we wish to charge this work to the
>>> context that initiated the async work and to impose concurrency limits
>>> based on the context.
>>>
>>> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
>>> ---
>>>    drivers/gpu/drm/i915/gem/i915_gem_context.c       | 4 ++++
>>>    drivers/gpu/drm/i915/gem/i915_gem_context.h       | 6 ++++++
>>>    drivers/gpu/drm/i915/gem/i915_gem_context_types.h | 6 ++++++
>>>    3 files changed, 16 insertions(+)
>>>
>>> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context.c b/drivers/gpu/drm/i915/gem/i915_gem_context.c
>>> index 41784df51e58..bd68746327b3 100644
>>> --- a/drivers/gpu/drm/i915/gem/i915_gem_context.c
>>> +++ b/drivers/gpu/drm/i915/gem/i915_gem_context.c
>>> @@ -714,6 +714,10 @@ __create_context(struct drm_i915_private *i915)
>>>        ctx->sched.priority = I915_USER_PRIORITY(I915_PRIORITY_NORMAL);
>>>        mutex_init(&ctx->mutex);
>>>    
>>> +     ctx->async.width = rounddown_pow_of_two(num_online_cpus());
>>> +     ctx->async.context = dma_fence_context_alloc(ctx->async.width);
>>> +     ctx->async.width--;
>>
>> Hey I had a tri-core CPU back in the day.. :) Really, I can only assume
>> you are oding some tricks with masks which maybe only work with power of
>> 2 num cpus? Hard to say.. please explain in a comment.
> 
> Just a pot mask, that fits in the currently available set of CPUs.
>   
>> I don't even understand what the context will be for yet and why it
>> needs a separate context id.
> 
> The longer term view is that I want to pull the various async tasks we
> use into a CPU scheduling kthread[s], that shares the same priority
> inheritance of tasks. The issue at the moment is that as we use the
> system_wq, that imposes an implicit FIFO ordering on our tasks upsetting
> our context priorities. This is a step towards that to start looking at
> how we might limit concurrency in various stages by using a bunch of
> timelines for each stage, and queuing our work along each timeline before
> submitting to an unbound system_wq. [The immediate goal is to limit how
> much of the CPU one client can hog by submitting deferred work that would
> run in parallel, with a view to making that configurable per-context.]

You are thinking of connecting the GEM context priority with task 
priority? Or create the async kthreads with the same task priority as 
the task who owns the GEM context has? Will that be too many kthreads? I 
suppose they would be created and destroyed on demand so maybe not.

> 
>>>        spin_lock_init(&ctx->stale.lock);
>>>        INIT_LIST_HEAD(&ctx->stale.engines);
>>>    
>>> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context.h b/drivers/gpu/drm/i915/gem/i915_gem_context.h
>>> index 3702b2fb27ab..e104ff0ae740 100644
>>> --- a/drivers/gpu/drm/i915/gem/i915_gem_context.h
>>> +++ b/drivers/gpu/drm/i915/gem/i915_gem_context.h
>>> @@ -134,6 +134,12 @@ int i915_gem_context_setparam_ioctl(struct drm_device *dev, void *data,
>>>    int i915_gem_context_reset_stats_ioctl(struct drm_device *dev, void *data,
>>>                                       struct drm_file *file);
>>>    
>>> +static inline u64 i915_gem_context_async_id(struct i915_gem_context *ctx)
>>> +{
>>> +     return (ctx->async.context +
>>> +             (atomic_fetch_inc(&ctx->async.cur) & ctx->async.width));
>>> +}
>>> +
>>>    static inline struct i915_gem_context *
>>>    i915_gem_context_get(struct i915_gem_context *ctx)
>>>    {
>>> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context_types.h b/drivers/gpu/drm/i915/gem/i915_gem_context_types.h
>>> index ae14ca24a11f..52561f98000f 100644
>>> --- a/drivers/gpu/drm/i915/gem/i915_gem_context_types.h
>>> +++ b/drivers/gpu/drm/i915/gem/i915_gem_context_types.h
>>> @@ -85,6 +85,12 @@ struct i915_gem_context {
>>>    
>>>        struct intel_timeline *timeline;
>>>    
>>> +     struct {
>>> +             u64 context;
>>> +             atomic_t cur;
>>
>> What is cur? In which patch it gets used? (Can't see it.)
> 
> See i915_gem_context_async_id() above.

Yeah found it later.

So in the patch where you use it, could you explain the significance of 
number of fence contexts vs the number of CPUs. What logic drives the 
choice of CPU concurrency per GEM context?

And what is the effective behaviour you get with N contexts - emit N 
concurrent operations and for N + 1 block in execbuf?

Regards,

Tvrtko
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Intel-gfx] [PATCH 09/20] drm/i915/gem: Assign context id for async work
  2020-07-08 14:24       ` Tvrtko Ursulin
@ 2020-07-08 15:36         ` Chris Wilson
  2020-07-09 11:01           ` Tvrtko Ursulin
  0 siblings, 1 reply; 65+ messages in thread
From: Chris Wilson @ 2020-07-08 15:36 UTC (permalink / raw)
  To: Tvrtko Ursulin, intel-gfx

Quoting Tvrtko Ursulin (2020-07-08 15:24:20)
> 
> On 08/07/2020 13:42, Chris Wilson wrote:
> > Quoting Tvrtko Ursulin (2020-07-08 13:26:24)
> >>
> >> On 06/07/2020 07:19, Chris Wilson wrote:
> >>> Allocate a few dma fence context id that we can use to associate async work
> >>> [for the CPU] launched on behalf of this context. For extra fun, we allow
> >>> a configurable concurrency width.
> >>>
> >>> A current example would be that we spawn an unbound worker for every
> >>> userptr get_pages. In the future, we wish to charge this work to the
> >>> context that initiated the async work and to impose concurrency limits
> >>> based on the context.
> >>>
> >>> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> >>> ---
> >>>    drivers/gpu/drm/i915/gem/i915_gem_context.c       | 4 ++++
> >>>    drivers/gpu/drm/i915/gem/i915_gem_context.h       | 6 ++++++
> >>>    drivers/gpu/drm/i915/gem/i915_gem_context_types.h | 6 ++++++
> >>>    3 files changed, 16 insertions(+)
> >>>
> >>> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context.c b/drivers/gpu/drm/i915/gem/i915_gem_context.c
> >>> index 41784df51e58..bd68746327b3 100644
> >>> --- a/drivers/gpu/drm/i915/gem/i915_gem_context.c
> >>> +++ b/drivers/gpu/drm/i915/gem/i915_gem_context.c
> >>> @@ -714,6 +714,10 @@ __create_context(struct drm_i915_private *i915)
> >>>        ctx->sched.priority = I915_USER_PRIORITY(I915_PRIORITY_NORMAL);
> >>>        mutex_init(&ctx->mutex);
> >>>    
> >>> +     ctx->async.width = rounddown_pow_of_two(num_online_cpus());
> >>> +     ctx->async.context = dma_fence_context_alloc(ctx->async.width);
> >>> +     ctx->async.width--;
> >>
> >> Hey I had a tri-core CPU back in the day.. :) Really, I can only assume
> >> you are oding some tricks with masks which maybe only work with power of
> >> 2 num cpus? Hard to say.. please explain in a comment.
> > 
> > Just a pot mask, that fits in the currently available set of CPUs.
> >   
> >> I don't even understand what the context will be for yet and why it
> >> needs a separate context id.
> > 
> > The longer term view is that I want to pull the various async tasks we
> > use into a CPU scheduling kthread[s], that shares the same priority
> > inheritance of tasks. The issue at the moment is that as we use the
> > system_wq, that imposes an implicit FIFO ordering on our tasks upsetting
> > our context priorities. This is a step towards that to start looking at
> > how we might limit concurrency in various stages by using a bunch of
> > timelines for each stage, and queuing our work along each timeline before
> > submitting to an unbound system_wq. [The immediate goal is to limit how
> > much of the CPU one client can hog by submitting deferred work that would
> > run in parallel, with a view to making that configurable per-context.]
> 
> You are thinking of connecting the GEM context priority with task 
> priority? Or create the async kthreads with the same task priority as 
> the task who owns the GEM context has? Will that be too many kthreads? I 
> suppose they would be created and destroyed on demand so maybe not.

I'm thinking of having dedicated kthread task runners. Maybe adjusting
between midRT-prio and normal-prio depending on workload. The essence is
to simply replace the FIFO workqueue with our own priolists. (Running
the first task in the queue, hopefully each task is short enough so that
we really don't have to start thinking about making the tasks
preemptible.]

Then world domination.

But first something that works with/like kthread_worker.

> >>>        spin_lock_init(&ctx->stale.lock);
> >>>        INIT_LIST_HEAD(&ctx->stale.engines);
> >>>    
> >>> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context.h b/drivers/gpu/drm/i915/gem/i915_gem_context.h
> >>> index 3702b2fb27ab..e104ff0ae740 100644
> >>> --- a/drivers/gpu/drm/i915/gem/i915_gem_context.h
> >>> +++ b/drivers/gpu/drm/i915/gem/i915_gem_context.h
> >>> @@ -134,6 +134,12 @@ int i915_gem_context_setparam_ioctl(struct drm_device *dev, void *data,
> >>>    int i915_gem_context_reset_stats_ioctl(struct drm_device *dev, void *data,
> >>>                                       struct drm_file *file);
> >>>    
> >>> +static inline u64 i915_gem_context_async_id(struct i915_gem_context *ctx)
> >>> +{
> >>> +     return (ctx->async.context +
> >>> +             (atomic_fetch_inc(&ctx->async.cur) & ctx->async.width));
> >>> +}
> >>> +
> >>>    static inline struct i915_gem_context *
> >>>    i915_gem_context_get(struct i915_gem_context *ctx)
> >>>    {
> >>> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context_types.h b/drivers/gpu/drm/i915/gem/i915_gem_context_types.h
> >>> index ae14ca24a11f..52561f98000f 100644
> >>> --- a/drivers/gpu/drm/i915/gem/i915_gem_context_types.h
> >>> +++ b/drivers/gpu/drm/i915/gem/i915_gem_context_types.h
> >>> @@ -85,6 +85,12 @@ struct i915_gem_context {
> >>>    
> >>>        struct intel_timeline *timeline;
> >>>    
> >>> +     struct {
> >>> +             u64 context;
> >>> +             atomic_t cur;
> >>
> >> What is cur? In which patch it gets used? (Can't see it.)
> > 
> > See i915_gem_context_async_id() above.
> 
> Yeah found it later.
> 
> So in the patch where you use it, could you explain the significance of 
> number of fence contexts vs the number of CPUs. What logic drives the 
> choice of CPU concurrency per GEM context?

Logic? Pick a number out of a hat.

> And what is the effective behaviour you get with N contexts - emit N 
> concurrent operations and for N + 1 block in execbuf?

Each context defines a timeline. A task is not ready to run until the
task before it in its timeline is completed. So we don't block in
execbuf, the scheduler waits until the request is ready before putting
it into the HW queues -- i.e. the number chain of fences with everything
that entails about ensuring it runs to completion [whether successfully
or not, if not we then rely on the error propagation to limit the damage
and report it back to the user if they kept a fence around to inspect].
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Intel-gfx] [PATCH 03/20] drm/i915/gem: Don't drop the timeline lock during execbuf
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 03/20] drm/i915/gem: Don't drop the timeline lock during execbuf Chris Wilson
@ 2020-07-08 16:54   ` Tvrtko Ursulin
  2020-07-08 18:08     ` Chris Wilson
  0 siblings, 1 reply; 65+ messages in thread
From: Tvrtko Ursulin @ 2020-07-08 16:54 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 06/07/2020 07:19, Chris Wilson wrote:
> Our timeline lock is our defence against a concurrent execbuf
> interrupting our request construction. we need hold it throughout or,
> for example, a second thread may interject a relocation request in
> between our own relocation request and execution in the ring.
> 
> A second, major benefit, is that it allows us to preserve a large chunk
> of the ringbuffer for our exclusive use; which should virtually
> eliminate the threat of hitting a wait_for_space during request
> construction -- although we should have already dropped other
> contentious locks at that point.
> 
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> ---
>   .../gpu/drm/i915/gem/i915_gem_execbuffer.c    | 241 ++++++++++++------
>   .../i915/gem/selftests/i915_gem_execbuffer.c  |  24 +-
>   2 files changed, 186 insertions(+), 79 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> index dd15a799f9d6..e4d06db3f313 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> @@ -259,6 +259,8 @@ struct i915_execbuffer {
>   		bool has_fence : 1;
>   		bool needs_unfenced : 1;
>   
> +		struct intel_context *ce;
> +
>   		struct i915_vma *target;
>   		struct i915_request *rq;
>   		struct i915_vma *rq_vma;
> @@ -639,6 +641,35 @@ static int eb_reserve_vma(const struct i915_execbuffer *eb,
>   	return 0;
>   }
>   
> +static void retire_requests(struct intel_timeline *tl, struct i915_request *end)
> +{
> +	struct i915_request *rq, *rn;
> +
> +	list_for_each_entry_safe(rq, rn, &tl->requests, link)
> +		if (rq == end || !i915_request_retire(rq))
> +			break;
> +}
> +
> +static int wait_for_timeline(struct intel_timeline *tl)
> +{
> +	do {
> +		struct dma_fence *fence;
> +		int err;
> +
> +		fence = i915_active_fence_get(&tl->last_request);
> +		if (!fence)
> +			return 0;
> +
> +		err = dma_fence_wait(fence, true);
> +		dma_fence_put(fence);
> +		if (err)
> +			return err;
> +
> +		/* Retiring may trigger a barrier, requiring an extra pass */
> +		retire_requests(tl, NULL);
> +	} while (1);
> +}
> +
>   static int eb_reserve(struct i915_execbuffer *eb)
>   {
>   	const unsigned int count = eb->buffer_count;
> @@ -646,7 +677,6 @@ static int eb_reserve(struct i915_execbuffer *eb)
>   	struct list_head last;
>   	struct eb_vma *ev;
>   	unsigned int i, pass;
> -	int err = 0;
>   
>   	/*
>   	 * Attempt to pin all of the buffers into the GTT.
> @@ -662,18 +692,22 @@ static int eb_reserve(struct i915_execbuffer *eb)
>   	 * room for the earlier objects *unless* we need to defragment.
>   	 */
>   
> -	if (mutex_lock_interruptible(&eb->i915->drm.struct_mutex))
> -		return -EINTR;
> -
>   	pass = 0;
>   	do {
> +		int err = 0;
> +
> +		if (mutex_lock_interruptible(&eb->i915->drm.struct_mutex))
> +			return -EINTR;

Recently you explained to me why we still use struct mutex here so 
maybe, while moving the code, document that in a comment.

> +
>   		list_for_each_entry(ev, &eb->unbound, bind_link) {
>   			err = eb_reserve_vma(eb, ev, pin_flags);
>   			if (err)
>   				break;
>   		}
> -		if (!(err == -ENOSPC || err == -EAGAIN))
> -			break;
> +		if (!(err == -ENOSPC || err == -EAGAIN)) {
> +			mutex_unlock(&eb->i915->drm.struct_mutex);
> +			return err;
> +		}
>   
>   		/* Resort *all* the objects into priority order */
>   		INIT_LIST_HEAD(&eb->unbound);
> @@ -702,11 +736,10 @@ static int eb_reserve(struct i915_execbuffer *eb)
>   				list_add_tail(&ev->bind_link, &last);
>   		}
>   		list_splice_tail(&last, &eb->unbound);
> +		mutex_unlock(&eb->i915->drm.struct_mutex);
>   
>   		if (err == -EAGAIN) {
> -			mutex_unlock(&eb->i915->drm.struct_mutex);
>   			flush_workqueue(eb->i915->mm.userptr_wq);
> -			mutex_lock(&eb->i915->drm.struct_mutex);
>   			continue;
>   		}
>   
> @@ -715,25 +748,23 @@ static int eb_reserve(struct i915_execbuffer *eb)
>   			break;
>   
>   		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);
> +			/*
> +			 * Too fragmented, retire everything on the timeline
> +			 * and so make it all [contexts included] available to
> +			 * evict.
> +			 */
> +			err = wait_for_timeline(eb->context->timeline);
>   			if (err)
> -				goto unlock;
> +				return err;
> +
>   			break;
>   
>   		default:
> -			err = -ENOSPC;
> -			goto unlock;
> +			return -ENOSPC;
>   		}
>   
>   		pin_flags = PIN_USER;
>   	} while (1);
> -
> -unlock:
> -	mutex_unlock(&eb->i915->drm.struct_mutex);
> -	return err;
>   }
>   
>   static unsigned int eb_batch_index(const struct i915_execbuffer *eb)
> @@ -1007,13 +1038,44 @@ static int reloc_gpu_chain(struct reloc_cache *cache)
>   	return err;
>   }
>   
> +static struct i915_request *
> +nested_request_create(struct intel_context *ce)
> +{
> +	struct i915_request *rq;
> +
> +	/* XXX This only works once; replace with shared timeline */

Once as in attempt to use the same local intel_context from another eb 
would upset lockdep? It's not a problem I think.

> +	mutex_lock_nested(&ce->timeline->mutex, SINGLE_DEPTH_NESTING);
> +	intel_context_enter(ce);
> +
> +	rq = __i915_request_create(ce, GFP_KERNEL);
> +
> +	intel_context_exit(ce);
> +	if (IS_ERR(rq))
> +		mutex_unlock(&ce->timeline->mutex);
> +
> +	return rq;
> +}
> +
> +static void __i915_request_add(struct i915_request *rq,
> +			       struct i915_sched_attr *attr)
> +{
> +	struct intel_timeline * const tl = i915_request_timeline(rq);
> +
> +	lockdep_assert_held(&tl->mutex);
> +	lockdep_unpin_lock(&tl->mutex, rq->cookie);
> +
> +	__i915_request_commit(rq);
> +	__i915_request_queue(rq, attr);
> +}
> +
>   static unsigned int reloc_bb_flags(const struct reloc_cache *cache)
>   {
>   	return cache->gen > 5 ? 0 : I915_DISPATCH_SECURE;
>   }
>   
> -static int reloc_gpu_flush(struct reloc_cache *cache)
> +static int reloc_gpu_flush(struct i915_execbuffer *eb)
>   {
> +	struct reloc_cache *cache = &eb->reloc_cache;
>   	struct i915_request *rq;
>   	int err;
>   
> @@ -1044,7 +1106,9 @@ static int reloc_gpu_flush(struct reloc_cache *cache)
>   		i915_request_set_error_once(rq, err);
>   
>   	intel_gt_chipset_flush(rq->engine->gt);
> -	i915_request_add(rq);
> +	__i915_request_add(rq, &eb->gem_context->sched);
> +	if (i915_request_timeline(rq) != eb->context->timeline)
> +		mutex_unlock(&i915_request_timeline(rq)->mutex);
>   
>   	return err;
>   }
> @@ -1103,27 +1167,15 @@ static int __reloc_gpu_alloc(struct i915_execbuffer *eb,
>   	if (err)
>   		goto err_unmap;
>   
> -	if (engine == eb->context->engine) {
> -		rq = i915_request_create(eb->context);
> -	} else {
> -		struct intel_context *ce;
> -
> -		ce = intel_context_create(engine);
> -		if (IS_ERR(ce)) {
> -			err = PTR_ERR(ce);
> -			goto err_unpin;
> -		}
> -
> -		i915_vm_put(ce->vm);
> -		ce->vm = i915_vm_get(eb->context->vm);
> -
> -		rq = intel_context_create_request(ce);
> -		intel_context_put(ce);
> -	}
> +	if (cache->ce == eb->context)
> +		rq = __i915_request_create(cache->ce, GFP_KERNEL);
> +	else
> +		rq = nested_request_create(cache->ce);
>   	if (IS_ERR(rq)) {
>   		err = PTR_ERR(rq);
>   		goto err_unpin;
>   	}
> +	rq->cookie = lockdep_pin_lock(&i915_request_timeline(rq)->mutex);
>   
>   	err = intel_gt_buffer_pool_mark_active(pool, rq);
>   	if (err)
> @@ -1151,7 +1203,9 @@ static int __reloc_gpu_alloc(struct i915_execbuffer *eb,
>   skip_request:
>   	i915_request_set_error_once(rq, err);
>   err_request:
> -	i915_request_add(rq);
> +	__i915_request_add(rq, &eb->gem_context->sched);
> +	if (i915_request_timeline(rq) != eb->context->timeline)
> +		mutex_unlock(&i915_request_timeline(rq)->mutex);
>   err_unpin:
>   	i915_vma_unpin(batch);
>   err_unmap:
> @@ -1161,11 +1215,6 @@ static int __reloc_gpu_alloc(struct i915_execbuffer *eb,
>   	return err;
>   }
>   
> -static bool reloc_can_use_engine(const struct intel_engine_cs *engine)
> -{
> -	return engine->class != VIDEO_DECODE_CLASS || !IS_GEN(engine->i915, 6);
> -}
> -
>   static u32 *reloc_gpu(struct i915_execbuffer *eb,
>   		      struct i915_vma *vma,
>   		      unsigned int len)
> @@ -1177,12 +1226,6 @@ static u32 *reloc_gpu(struct i915_execbuffer *eb,
>   	if (unlikely(!cache->rq)) {
>   		struct intel_engine_cs *engine = eb->engine;
>   
> -		if (!reloc_can_use_engine(engine)) {
> -			engine = engine->gt->engine_class[COPY_ENGINE_CLASS][0];
> -			if (!engine)
> -				return ERR_PTR(-ENODEV);
> -		}
> -
>   		err = __reloc_gpu_alloc(eb, engine, len);
>   		if (unlikely(err))
>   			return ERR_PTR(err);
> @@ -1513,7 +1556,7 @@ static int eb_relocate(struct i915_execbuffer *eb)
>   				break;
>   		}
>   
> -		flush = reloc_gpu_flush(&eb->reloc_cache);
> +		flush = reloc_gpu_flush(eb);
>   		if (!err)
>   			err = flush;
>   	}
> @@ -1737,21 +1780,17 @@ parser_mark_active(struct eb_parse_work *pw, struct intel_timeline *tl)
>   {
>   	int err;
>   
> -	mutex_lock(&tl->mutex);
> -
>   	err = __parser_mark_active(pw->shadow, tl, &pw->base.dma);
>   	if (err)
> -		goto unlock;
> +		return err;
>   
>   	if (pw->trampoline) {
>   		err = __parser_mark_active(pw->trampoline, tl, &pw->base.dma);
>   		if (err)
> -			goto unlock;
> +			return err;
>   	}
>   
> -unlock:
> -	mutex_unlock(&tl->mutex);
> -	return err;
> +	return 0;
>   }
>   
>   static int eb_parse_pipeline(struct i915_execbuffer *eb,
> @@ -2044,6 +2083,54 @@ static struct i915_request *eb_throttle(struct intel_context *ce)
>   	return i915_request_get(rq);
>   }
>   
> +static bool reloc_can_use_engine(const struct intel_engine_cs *engine)
> +{
> +	return engine->class != VIDEO_DECODE_CLASS || !IS_GEN(engine->i915, 6);
> +}
> +
> +static int __eb_pin_reloc_engine(struct i915_execbuffer *eb)
> +{
> +	struct intel_engine_cs *engine = eb->engine;
> +	struct intel_context *ce;
> +	int err;
> +
> +	if (reloc_can_use_engine(engine)) {
> +		eb->reloc_cache.ce = eb->context;
> +		return 0;
> +	}
> +
> +	engine = engine->gt->engine_class[COPY_ENGINE_CLASS][0];
> +	if (!engine)
> +		return -ENODEV;
> +
> +	ce = intel_context_create(engine);
> +	if (IS_ERR(ce))
> +		return PTR_ERR(ce);
> +
> +	/* Reuse eb->context->timeline with scheduler! */
> +
> +	i915_vm_put(ce->vm);
> +	ce->vm = i915_vm_get(eb->context->vm);
> +
> +	err = intel_context_pin(ce);
> +	if (err)
> +		return err;
> +
> +	eb->reloc_cache.ce = ce;
> +	return 0;
> +}
> +
> +static void __eb_unpin_reloc_engine(struct i915_execbuffer *eb)
> +{
> +	struct intel_context *ce = eb->reloc_cache.ce;
> +
> +	if (ce == eb->context)
> +		return;
> +
> +	intel_context_unpin(ce);
> +	intel_context_put(ce);
> +}
> +
>   static int __eb_pin_engine(struct i915_execbuffer *eb, struct intel_context *ce)
>   {
>   	struct intel_timeline *tl;
> @@ -2087,9 +2174,7 @@ static int __eb_pin_engine(struct i915_execbuffer *eb, struct intel_context *ce)
>   	intel_context_enter(ce);
>   	rq = eb_throttle(ce);
>   
> -	intel_context_timeline_unlock(tl);
> -
> -	if (rq) {
> +	while (rq) {
>   		bool nonblock = eb->file->filp->f_flags & O_NONBLOCK;
>   		long timeout;
>   
> @@ -2097,23 +2182,34 @@ static int __eb_pin_engine(struct i915_execbuffer *eb, struct intel_context *ce)
>   		if (nonblock)
>   			timeout = 0;
>   
> +		mutex_unlock(&tl->mutex);

"Don't drop the timeline lock during execbuf"? Is the "during execbuf" 
actually a smaller subset

> +
>   		timeout = i915_request_wait(rq,
>   					    I915_WAIT_INTERRUPTIBLE,
>   					    timeout);
>   		i915_request_put(rq);
>   
> +		mutex_lock(&tl->mutex);
> +
>   		if (timeout < 0) {
>   			err = nonblock ? -EWOULDBLOCK : timeout;
>   			goto err_exit;
>   		}
> +
> +		retire_requests(tl, NULL);
> +		rq = eb_throttle(ce);

Alternative to avoid two call sites to eb_throttle of

   while (rq = eb_throttle(ce)) {

Or checkpatch does not like it?

>   	}
>   
>   	eb->engine = ce->engine;
>   	eb->context = ce;
> +
> +	err = __eb_pin_reloc_engine(eb);
> +	if (err)
> +		goto err_exit;
> +
>   	return 0;
>   
>   err_exit:
> -	mutex_lock(&tl->mutex);
>   	intel_context_exit(ce);
>   	intel_context_timeline_unlock(tl);
>   err_unpin:
> @@ -2124,11 +2220,11 @@ static int __eb_pin_engine(struct i915_execbuffer *eb, struct intel_context *ce)
>   static void eb_unpin_engine(struct i915_execbuffer *eb)
>   {
>   	struct intel_context *ce = eb->context;
> -	struct intel_timeline *tl = ce->timeline;
>   
> -	mutex_lock(&tl->mutex);
> +	__eb_unpin_reloc_engine(eb);
> +
>   	intel_context_exit(ce);
> -	mutex_unlock(&tl->mutex);
> +	intel_context_timeline_unlock(ce->timeline);
>   
>   	intel_context_unpin(ce);
>   }
> @@ -2330,15 +2426,6 @@ signal_fence_array(struct i915_execbuffer *eb,
>   	}
>   }
>   
> -static void retire_requests(struct intel_timeline *tl, struct i915_request *end)
> -{
> -	struct i915_request *rq, *rn;
> -
> -	list_for_each_entry_safe(rq, rn, &tl->requests, link)
> -		if (rq == end || !i915_request_retire(rq))
> -			break;
> -}
> -
>   static void eb_request_add(struct i915_execbuffer *eb)
>   {
>   	struct i915_request *rq = eb->request;
> @@ -2367,8 +2454,6 @@ static void eb_request_add(struct i915_execbuffer *eb)
>   	/* Try to clean up the client's timeline after submitting the request */
>   	if (prev)
>   		retire_requests(tl, prev);
> -
> -	mutex_unlock(&tl->mutex);
>   }
>   
>   static int
> @@ -2455,6 +2540,7 @@ i915_gem_do_execbuffer(struct drm_device *dev,
>   	err = eb_pin_engine(&eb, file, args);
>   	if (unlikely(err))
>   		goto err_context;
> +	lockdep_assert_held(&eb.context->timeline->mutex);
>   
>   	err = eb_relocate(&eb);
>   	if (err) {
> @@ -2522,11 +2608,12 @@ i915_gem_do_execbuffer(struct drm_device *dev,
>   	GEM_BUG_ON(eb.reloc_cache.rq);
>   
>   	/* Allocate a request for this batch buffer nice and early. */
> -	eb.request = i915_request_create(eb.context);
> +	eb.request = __i915_request_create(eb.context, GFP_KERNEL);
>   	if (IS_ERR(eb.request)) {
>   		err = PTR_ERR(eb.request);
>   		goto err_batch_unpin;
>   	}
> +	eb.request->cookie = lockdep_pin_lock(&eb.context->timeline->mutex);
>   
>   	if (in_fence) {
>   		if (args->flags & I915_EXEC_FENCE_SUBMIT)
> diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_execbuffer.c
> index 57c14d3340cd..992d46db1b33 100644
> --- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_execbuffer.c
> +++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_execbuffer.c
> @@ -7,6 +7,9 @@
>   
>   #include "gt/intel_engine_pm.h"
>   #include "selftests/igt_flush_test.h"
> +#include "selftests/mock_drm.h"
> +
> +#include "mock_context.h"
>   
>   static u64 read_reloc(const u32 *map, int x, const u64 mask)
>   {
> @@ -60,7 +63,7 @@ static int __igt_gpu_reloc(struct i915_execbuffer *eb,
>   
>   	GEM_BUG_ON(!eb->reloc_cache.rq);
>   	rq = i915_request_get(eb->reloc_cache.rq);
> -	err = reloc_gpu_flush(&eb->reloc_cache);
> +	err = reloc_gpu_flush(eb);
>   	if (err)
>   		goto put_rq;
>   	GEM_BUG_ON(eb->reloc_cache.rq);
> @@ -100,14 +103,22 @@ static int igt_gpu_reloc(void *arg)
>   {
>   	struct i915_execbuffer eb;
>   	struct drm_i915_gem_object *scratch;
> +	struct file *file;
>   	int err = 0;
>   	u32 *map;
>   
> +	file = mock_file(arg);
> +	if (IS_ERR(file))
> +		return PTR_ERR(file);
> +
>   	eb.i915 = arg;
> +	eb.gem_context = live_context(arg, file);
> +	if (IS_ERR(eb.gem_context))
> +		goto err_file;
>   
>   	scratch = i915_gem_object_create_internal(eb.i915, 4096);
>   	if (IS_ERR(scratch))
> -		return PTR_ERR(scratch);
> +		goto err_file;
>   
>   	map = i915_gem_object_pin_map(scratch, I915_MAP_WC);
>   	if (IS_ERR(map)) {
> @@ -130,8 +141,15 @@ static int igt_gpu_reloc(void *arg)
>   		if (err)
>   			goto err_put;
>   
> +		mutex_lock(&eb.context->timeline->mutex);
> +		intel_context_enter(eb.context);
> +		eb.reloc_cache.ce = eb.context;
> +
>   		err = __igt_gpu_reloc(&eb, scratch);
>   
> +		intel_context_exit(eb.context);
> +		mutex_unlock(&eb.context->timeline->mutex);
> +
>   		intel_context_unpin(eb.context);
>   err_put:
>   		intel_context_put(eb.context);
> @@ -146,6 +164,8 @@ static int igt_gpu_reloc(void *arg)
>   
>   err_scratch:
>   	i915_gem_object_put(scratch);
> +err_file:
> +	fput(file);
>   	return err;
>   }
>   
> 

Regards,

Tvrtko
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Intel-gfx] [PATCH 03/20] drm/i915/gem: Don't drop the timeline lock during execbuf
  2020-07-08 16:54   ` Tvrtko Ursulin
@ 2020-07-08 18:08     ` Chris Wilson
  2020-07-09 10:52       ` Tvrtko Ursulin
  0 siblings, 1 reply; 65+ messages in thread
From: Chris Wilson @ 2020-07-08 18:08 UTC (permalink / raw)
  To: Tvrtko Ursulin, intel-gfx

Quoting Tvrtko Ursulin (2020-07-08 17:54:51)
> 
> On 06/07/2020 07:19, Chris Wilson wrote:
> > @@ -662,18 +692,22 @@ static int eb_reserve(struct i915_execbuffer *eb)
> >        * room for the earlier objects *unless* we need to defragment.
> >        */
> >   
> > -     if (mutex_lock_interruptible(&eb->i915->drm.struct_mutex))
> > -             return -EINTR;
> > -
> >       pass = 0;
> >       do {
> > +             int err = 0;
> > +
> > +             if (mutex_lock_interruptible(&eb->i915->drm.struct_mutex))
> > +                     return -EINTR;
> 
> Recently you explained to me why we still use struct mutex here so 
> maybe, while moving the code, document that in a comment.

Part of the work here is to eliminate the need for the struct_mutex,
that will be replaced by not dropping the vm->mutex while binding
multiple vma.

It's the interaction with the waits to flush other vm users when under
pressure that are the most annoying. This area is not straightforward,
and at least deserves some comments so that the thinking behind it can
be fixed.

> > +static struct i915_request *
> > +nested_request_create(struct intel_context *ce)
> > +{
> > +     struct i915_request *rq;
> > +
> > +     /* XXX This only works once; replace with shared timeline */
> 
> Once as in attempt to use the same local intel_context from another eb 
> would upset lockdep? It's not a problem I think.

"Once" as in this is the only time we can do this nested locking between
engines of the same context in the whole driver, or else lockdep would
have been right to complain. [i.e. if we ever do the reserve nesting, we
are screwed.]

Fwiw, I have posted patches that will eliminate the need for a nested
timeline here :)

> > +     mutex_lock_nested(&ce->timeline->mutex, SINGLE_DEPTH_NESTING);
> > +     intel_context_enter(ce);


> >   static int __eb_pin_engine(struct i915_execbuffer *eb, struct intel_context *ce)
> >   {
> >       struct intel_timeline *tl;
> > @@ -2087,9 +2174,7 @@ static int __eb_pin_engine(struct i915_execbuffer *eb, struct intel_context *ce)
> >       intel_context_enter(ce);
> >       rq = eb_throttle(ce);
> >   
> > -     intel_context_timeline_unlock(tl);
> > -
> > -     if (rq) {
> > +     while (rq) {
> >               bool nonblock = eb->file->filp->f_flags & O_NONBLOCK;
> >               long timeout;
> >   
> > @@ -2097,23 +2182,34 @@ static int __eb_pin_engine(struct i915_execbuffer *eb, struct intel_context *ce)
> >               if (nonblock)
> >                       timeout = 0;
> >   
> > +             mutex_unlock(&tl->mutex);
> 
> "Don't drop the timeline lock during execbuf"? Is the "during execbuf" 
> actually a smaller subset

We are before execbuf in my book :)

This is throttle the hog before we start, and reserve enough space in
the ring (we make sure there's a page, or thereabouts) to build a batch
without interruption.
 
> > +
> >               timeout = i915_request_wait(rq,
> >                                           I915_WAIT_INTERRUPTIBLE,
> >                                           timeout);
> >               i915_request_put(rq);
> >   
> > +             mutex_lock(&tl->mutex);
> > +
> >               if (timeout < 0) {
> >                       err = nonblock ? -EWOULDBLOCK : timeout;
> >                       goto err_exit;
> >               }
> > +
> > +             retire_requests(tl, NULL);
> > +             rq = eb_throttle(ce);
> 
> Alternative to avoid two call sites to eb_throttle of
> 
>    while (rq = eb_throttle(ce)) {
> 
> Or checkpatch does not like it?

Ta, that loop was annoying me, and I couldn't quite put my finger on
what.

checkpatch.pl --strict is quiet. Appears it only hates if (x = y).
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Intel-gfx] [PATCH 03/20] drm/i915/gem: Don't drop the timeline lock during execbuf
  2020-07-08 18:08     ` Chris Wilson
@ 2020-07-09 10:52       ` Tvrtko Ursulin
  2020-07-09 10:57         ` Chris Wilson
  0 siblings, 1 reply; 65+ messages in thread
From: Tvrtko Ursulin @ 2020-07-09 10:52 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 08/07/2020 19:08, Chris Wilson wrote:
> Quoting Tvrtko Ursulin (2020-07-08 17:54:51)
>>
>> On 06/07/2020 07:19, Chris Wilson wrote:
>>> @@ -662,18 +692,22 @@ static int eb_reserve(struct i915_execbuffer *eb)
>>>         * room for the earlier objects *unless* we need to defragment.
>>>         */
>>>    
>>> -     if (mutex_lock_interruptible(&eb->i915->drm.struct_mutex))
>>> -             return -EINTR;
>>> -
>>>        pass = 0;
>>>        do {
>>> +             int err = 0;
>>> +
>>> +             if (mutex_lock_interruptible(&eb->i915->drm.struct_mutex))
>>> +                     return -EINTR;
>>
>> Recently you explained to me why we still use struct mutex here so
>> maybe, while moving the code, document that in a comment.
> 
> Part of the work here is to eliminate the need for the struct_mutex,
> that will be replaced by not dropping the vm->mutex while binding
> multiple vma.
> 
> It's the interaction with the waits to flush other vm users when under
> pressure that are the most annoying. This area is not straightforward,
> and at least deserves some comments so that the thinking behind it can
> be fixed.
> 
>>> +static struct i915_request *
>>> +nested_request_create(struct intel_context *ce)
>>> +{
>>> +     struct i915_request *rq;
>>> +
>>> +     /* XXX This only works once; replace with shared timeline */
>>
>> Once as in attempt to use the same local intel_context from another eb
>> would upset lockdep? It's not a problem I think.
> 
> "Once" as in this is the only time we can do this nested locking between
> engines of the same context in the whole driver, or else lockdep would
> have been right to complain. [i.e. if we ever do the reserve nesting, we
> are screwed.]
> 
> Fwiw, I have posted patches that will eliminate the need for a nested
> timeline here :)

In this series or just on the mailing list?

> 
>>> +     mutex_lock_nested(&ce->timeline->mutex, SINGLE_DEPTH_NESTING);
>>> +     intel_context_enter(ce);
> 
> 
>>>    static int __eb_pin_engine(struct i915_execbuffer *eb, struct intel_context *ce)
>>>    {
>>>        struct intel_timeline *tl;
>>> @@ -2087,9 +2174,7 @@ static int __eb_pin_engine(struct i915_execbuffer *eb, struct intel_context *ce)
>>>        intel_context_enter(ce);
>>>        rq = eb_throttle(ce);
>>>    
>>> -     intel_context_timeline_unlock(tl);
>>> -
>>> -     if (rq) {
>>> +     while (rq) {
>>>                bool nonblock = eb->file->filp->f_flags & O_NONBLOCK;
>>>                long timeout;
>>>    
>>> @@ -2097,23 +2182,34 @@ static int __eb_pin_engine(struct i915_execbuffer *eb, struct intel_context *ce)
>>>                if (nonblock)
>>>                        timeout = 0;
>>>    
>>> +             mutex_unlock(&tl->mutex);
>>
>> "Don't drop the timeline lock during execbuf"? Is the "during execbuf"
>> actually a smaller subset
> 
> We are before execbuf in my book :)
> 
> This is throttle the hog before we start, and reserve enough space in
> the ring (we make sure there's a page, or thereabouts) to build a batch
> without interruption.

Ok. :)

Regards,

Tvrtko
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Intel-gfx] [PATCH 03/20] drm/i915/gem: Don't drop the timeline lock during execbuf
  2020-07-09 10:52       ` Tvrtko Ursulin
@ 2020-07-09 10:57         ` Chris Wilson
  0 siblings, 0 replies; 65+ messages in thread
From: Chris Wilson @ 2020-07-09 10:57 UTC (permalink / raw)
  To: Tvrtko Ursulin, intel-gfx

Quoting Tvrtko Ursulin (2020-07-09 11:52:19)
> 
> On 08/07/2020 19:08, Chris Wilson wrote:
> > Quoting Tvrtko Ursulin (2020-07-08 17:54:51)
> >>
> >> On 06/07/2020 07:19, Chris Wilson wrote:
> >>> +static struct i915_request *
> >>> +nested_request_create(struct intel_context *ce)
> >>> +{
> >>> +     struct i915_request *rq;
> >>> +
> >>> +     /* XXX This only works once; replace with shared timeline */
> >>
> >> Once as in attempt to use the same local intel_context from another eb
> >> would upset lockdep? It's not a problem I think.
> > 
> > "Once" as in this is the only time we can do this nested locking between
> > engines of the same context in the whole driver, or else lockdep would
> > have been right to complain. [i.e. if we ever do the reserve nesting, we
> > are screwed.]
> > 
> > Fwiw, I have posted patches that will eliminate the need for a nested
> > timeline here :)
> 
> In this series or just on the mailing list?

It's the implement a ring-scheduler for gen6 series, which is currently
sitting at the end of the vdeadline scheduler series.
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Intel-gfx] [PATCH 09/20] drm/i915/gem: Assign context id for async work
  2020-07-08 15:36         ` Chris Wilson
@ 2020-07-09 11:01           ` Tvrtko Ursulin
  2020-07-09 11:07             ` Chris Wilson
  0 siblings, 1 reply; 65+ messages in thread
From: Tvrtko Ursulin @ 2020-07-09 11:01 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 08/07/2020 16:36, Chris Wilson wrote:
> Quoting Tvrtko Ursulin (2020-07-08 15:24:20)
>>
>> On 08/07/2020 13:42, Chris Wilson wrote:
>>> Quoting Tvrtko Ursulin (2020-07-08 13:26:24)
>>>>
>>>> On 06/07/2020 07:19, Chris Wilson wrote:
>>>>> Allocate a few dma fence context id that we can use to associate async work
>>>>> [for the CPU] launched on behalf of this context. For extra fun, we allow
>>>>> a configurable concurrency width.
>>>>>
>>>>> A current example would be that we spawn an unbound worker for every
>>>>> userptr get_pages. In the future, we wish to charge this work to the
>>>>> context that initiated the async work and to impose concurrency limits
>>>>> based on the context.
>>>>>
>>>>> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
>>>>> ---
>>>>>     drivers/gpu/drm/i915/gem/i915_gem_context.c       | 4 ++++
>>>>>     drivers/gpu/drm/i915/gem/i915_gem_context.h       | 6 ++++++
>>>>>     drivers/gpu/drm/i915/gem/i915_gem_context_types.h | 6 ++++++
>>>>>     3 files changed, 16 insertions(+)
>>>>>
>>>>> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context.c b/drivers/gpu/drm/i915/gem/i915_gem_context.c
>>>>> index 41784df51e58..bd68746327b3 100644
>>>>> --- a/drivers/gpu/drm/i915/gem/i915_gem_context.c
>>>>> +++ b/drivers/gpu/drm/i915/gem/i915_gem_context.c
>>>>> @@ -714,6 +714,10 @@ __create_context(struct drm_i915_private *i915)
>>>>>         ctx->sched.priority = I915_USER_PRIORITY(I915_PRIORITY_NORMAL);
>>>>>         mutex_init(&ctx->mutex);
>>>>>     
>>>>> +     ctx->async.width = rounddown_pow_of_two(num_online_cpus());
>>>>> +     ctx->async.context = dma_fence_context_alloc(ctx->async.width);
>>>>> +     ctx->async.width--;
>>>>
>>>> Hey I had a tri-core CPU back in the day.. :) Really, I can only assume
>>>> you are oding some tricks with masks which maybe only work with power of
>>>> 2 num cpus? Hard to say.. please explain in a comment.
>>>
>>> Just a pot mask, that fits in the currently available set of CPUs.
>>>    
>>>> I don't even understand what the context will be for yet and why it
>>>> needs a separate context id.
>>>
>>> The longer term view is that I want to pull the various async tasks we
>>> use into a CPU scheduling kthread[s], that shares the same priority
>>> inheritance of tasks. The issue at the moment is that as we use the
>>> system_wq, that imposes an implicit FIFO ordering on our tasks upsetting
>>> our context priorities. This is a step towards that to start looking at
>>> how we might limit concurrency in various stages by using a bunch of
>>> timelines for each stage, and queuing our work along each timeline before
>>> submitting to an unbound system_wq. [The immediate goal is to limit how
>>> much of the CPU one client can hog by submitting deferred work that would
>>> run in parallel, with a view to making that configurable per-context.]
>>
>> You are thinking of connecting the GEM context priority with task
>> priority? Or create the async kthreads with the same task priority as
>> the task who owns the GEM context has? Will that be too many kthreads? I
>> suppose they would be created and destroyed on demand so maybe not.
> 
> I'm thinking of having dedicated kthread task runners. Maybe adjusting
> between midRT-prio and normal-prio depending on workload. The essence is
> to simply replace the FIFO workqueue with our own priolists. (Running
> the first task in the queue, hopefully each task is short enough so that
> we really don't have to start thinking about making the tasks
> preemptible.]
> 
> Then world domination.
> 
> But first something that works with/like kthread_worker.
> 
>>>>>         spin_lock_init(&ctx->stale.lock);
>>>>>         INIT_LIST_HEAD(&ctx->stale.engines);
>>>>>     
>>>>> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context.h b/drivers/gpu/drm/i915/gem/i915_gem_context.h
>>>>> index 3702b2fb27ab..e104ff0ae740 100644
>>>>> --- a/drivers/gpu/drm/i915/gem/i915_gem_context.h
>>>>> +++ b/drivers/gpu/drm/i915/gem/i915_gem_context.h
>>>>> @@ -134,6 +134,12 @@ int i915_gem_context_setparam_ioctl(struct drm_device *dev, void *data,
>>>>>     int i915_gem_context_reset_stats_ioctl(struct drm_device *dev, void *data,
>>>>>                                        struct drm_file *file);
>>>>>     
>>>>> +static inline u64 i915_gem_context_async_id(struct i915_gem_context *ctx)
>>>>> +{
>>>>> +     return (ctx->async.context +
>>>>> +             (atomic_fetch_inc(&ctx->async.cur) & ctx->async.width));
>>>>> +}
>>>>> +
>>>>>     static inline struct i915_gem_context *
>>>>>     i915_gem_context_get(struct i915_gem_context *ctx)
>>>>>     {
>>>>> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context_types.h b/drivers/gpu/drm/i915/gem/i915_gem_context_types.h
>>>>> index ae14ca24a11f..52561f98000f 100644
>>>>> --- a/drivers/gpu/drm/i915/gem/i915_gem_context_types.h
>>>>> +++ b/drivers/gpu/drm/i915/gem/i915_gem_context_types.h
>>>>> @@ -85,6 +85,12 @@ struct i915_gem_context {
>>>>>     
>>>>>         struct intel_timeline *timeline;
>>>>>     
>>>>> +     struct {
>>>>> +             u64 context;
>>>>> +             atomic_t cur;
>>>>
>>>> What is cur? In which patch it gets used? (Can't see it.)
>>>
>>> See i915_gem_context_async_id() above.
>>
>> Yeah found it later.
>>
>> So in the patch where you use it, could you explain the significance of
>> number of fence contexts vs the number of CPUs. What logic drives the
>> choice of CPU concurrency per GEM context?
> 
> Logic? Pick a number out of a hat.
> 
>> And what is the effective behaviour you get with N contexts - emit N
>> concurrent operations and for N + 1 block in execbuf?
> 
> Each context defines a timeline. A task is not ready to run until the
> task before it in its timeline is completed. So we don't block in
> execbuf, the scheduler waits until the request is ready before putting
> it into the HW queues -- i.e. the number chain of fences with everything
> that entails about ensuring it runs to completion [whether successfully
> or not, if not we then rely on the error propagation to limit the damage
> and report it back to the user if they kept a fence around to inspect].

Okay but what is the benefit of N contexts in this series, before the 
work is actually spread over ctx async width CPUs? Is there any? If not 
I would prefer this patch is delayed until the time some actual 
parallelism is ready to be added.

Regards,

Tvrtko
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Intel-gfx] [PATCH 09/20] drm/i915/gem: Assign context id for async work
  2020-07-09 11:01           ` Tvrtko Ursulin
@ 2020-07-09 11:07             ` Chris Wilson
  2020-07-09 11:59               ` Tvrtko Ursulin
  0 siblings, 1 reply; 65+ messages in thread
From: Chris Wilson @ 2020-07-09 11:07 UTC (permalink / raw)
  To: Tvrtko Ursulin, intel-gfx

Quoting Tvrtko Ursulin (2020-07-09 12:01:29)
> 
> On 08/07/2020 16:36, Chris Wilson wrote:
> > Quoting Tvrtko Ursulin (2020-07-08 15:24:20)
> >> And what is the effective behaviour you get with N contexts - emit N
> >> concurrent operations and for N + 1 block in execbuf?
> > 
> > Each context defines a timeline. A task is not ready to run until the
> > task before it in its timeline is completed. So we don't block in
> > execbuf, the scheduler waits until the request is ready before putting
> > it into the HW queues -- i.e. the number chain of fences with everything
> > that entails about ensuring it runs to completion [whether successfully
> > or not, if not we then rely on the error propagation to limit the damage
> > and report it back to the user if they kept a fence around to inspect].
> 
> Okay but what is the benefit of N contexts in this series, before the 
> work is actually spread over ctx async width CPUs? Is there any? If not 
> I would prefer this patch is delayed until the time some actual 
> parallelism is ready to be added.

We currently submit an unbounded amount of work. This patch is added
along with its user to restrict the amount of work allowed to run in
parallel, and also is used to [crudely] serialise the multiple threads
attempting to allocate space in the vm when we completely exhaust that
address space. We need at least one fence-context id for each user, this
took the opportunity to generalise that to N ids for each user.
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Intel-gfx] [PATCH 09/20] drm/i915/gem: Assign context id for async work
  2020-07-09 11:07             ` Chris Wilson
@ 2020-07-09 11:59               ` Tvrtko Ursulin
  2020-07-09 12:07                 ` Chris Wilson
  0 siblings, 1 reply; 65+ messages in thread
From: Tvrtko Ursulin @ 2020-07-09 11:59 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 09/07/2020 12:07, Chris Wilson wrote:
> Quoting Tvrtko Ursulin (2020-07-09 12:01:29)
>>
>> On 08/07/2020 16:36, Chris Wilson wrote:
>>> Quoting Tvrtko Ursulin (2020-07-08 15:24:20)
>>>> And what is the effective behaviour you get with N contexts - emit N
>>>> concurrent operations and for N + 1 block in execbuf?
>>>
>>> Each context defines a timeline. A task is not ready to run until the
>>> task before it in its timeline is completed. So we don't block in
>>> execbuf, the scheduler waits until the request is ready before putting
>>> it into the HW queues -- i.e. the number chain of fences with everything
>>> that entails about ensuring it runs to completion [whether successfully
>>> or not, if not we then rely on the error propagation to limit the damage
>>> and report it back to the user if they kept a fence around to inspect].
>>
>> Okay but what is the benefit of N contexts in this series, before the
>> work is actually spread over ctx async width CPUs? Is there any? If not
>> I would prefer this patch is delayed until the time some actual
>> parallelism is ready to be added.
> 
> We currently submit an unbounded amount of work. This patch is added
> along with its user to restrict the amount of work allowed to run in
> parallel, and also is used to [crudely] serialise the multiple threads
> attempting to allocate space in the vm when we completely exhaust that
> address space. We need at least one fence-context id for each user, this
> took the opportunity to generalise that to N ids for each user.

Right, this is what I asked at the beginning - restricting amount of 
work run in parallel - does mean there is some "blocking"/serialisation 
during execbuf? Or it is all async but then what is restricted?

Regards,

Tvrtko


_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Intel-gfx] [PATCH 09/20] drm/i915/gem: Assign context id for async work
  2020-07-09 11:59               ` Tvrtko Ursulin
@ 2020-07-09 12:07                 ` Chris Wilson
  2020-07-13 12:22                   ` Tvrtko Ursulin
  0 siblings, 1 reply; 65+ messages in thread
From: Chris Wilson @ 2020-07-09 12:07 UTC (permalink / raw)
  To: Tvrtko Ursulin, intel-gfx

Quoting Tvrtko Ursulin (2020-07-09 12:59:51)
> 
> On 09/07/2020 12:07, Chris Wilson wrote:
> > Quoting Tvrtko Ursulin (2020-07-09 12:01:29)
> >>
> >> On 08/07/2020 16:36, Chris Wilson wrote:
> >>> Quoting Tvrtko Ursulin (2020-07-08 15:24:20)
> >>>> And what is the effective behaviour you get with N contexts - emit N
> >>>> concurrent operations and for N + 1 block in execbuf?
> >>>
> >>> Each context defines a timeline. A task is not ready to run until the
> >>> task before it in its timeline is completed. So we don't block in
> >>> execbuf, the scheduler waits until the request is ready before putting
> >>> it into the HW queues -- i.e. the number chain of fences with everything
> >>> that entails about ensuring it runs to completion [whether successfully
> >>> or not, if not we then rely on the error propagation to limit the damage
> >>> and report it back to the user if they kept a fence around to inspect].
> >>
> >> Okay but what is the benefit of N contexts in this series, before the
> >> work is actually spread over ctx async width CPUs? Is there any? If not
> >> I would prefer this patch is delayed until the time some actual
> >> parallelism is ready to be added.
> > 
> > We currently submit an unbounded amount of work. This patch is added
> > along with its user to restrict the amount of work allowed to run in
> > parallel, and also is used to [crudely] serialise the multiple threads
> > attempting to allocate space in the vm when we completely exhaust that
> > address space. We need at least one fence-context id for each user, this
> > took the opportunity to generalise that to N ids for each user.
> 
> Right, this is what I asked at the beginning - restricting amount of 
> work run in parallel - does mean there is some "blocking"/serialisation 
> during execbuf? Or it is all async but then what is restricted?

It's all* async, so the number of workqueues we utilise is restricted,
and so limits the number of CPUs we allow the one context to spread
across with multiple execbufs.

*fsvo all.
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Intel-gfx] [PATCH 19/20] drm/i915/gem: Replace i915_gem_object.mm.mutex with reservation_ww_class
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 19/20] drm/i915/gem: Replace i915_gem_object.mm.mutex with reservation_ww_class Chris Wilson
@ 2020-07-09 14:06   ` Maarten Lankhorst
  0 siblings, 0 replies; 65+ messages in thread
From: Maarten Lankhorst @ 2020-07-09 14:06 UTC (permalink / raw)
  To: Chris Wilson; +Cc: intel-gfx

Op 06-07-2020 om 08:19 schreef Chris Wilson:
> Our goal is to pull all memory reservations (next iteration
> obj->ops->get_pages()) under a ww_mutex, and to align those reservations
> with other drivers, i.e. control all such allocations with the
> reservation_ww_class. Currently, this is under the purview of the
> obj->mm.mutex, and while obj->mm remains an embedded struct we can
> "simply" switch to using the reservation_ww_class obj->base.resv->lock
>
> The major consequence is the impact on the shrinker paths as the
> reservation_ww_class is used to wrap allocations, and a ww_mutex does
> not support subclassing so we cannot do our usual trick of knowing that
> we never recurse inside the shrinker and instead have to finish the
> reclaim with a trylock. This may result in us failing to release the
> pages after having released the vma. This will have to do until a better
> idea comes along.
>
> However, this step only converts the mutex over and continues to treat
> everything as a single allocation and pinning the pages. With the
> ww_mutex in place we can remove the temporary pinning, as we can then
> reserve all storage en masse.
>
> One last thing to do: kill the implict page pinning for active vma.
> This will require us to invalidate the vma->pages when the backing store
> is removed (and we expect that while the vma is active, we mark the
> backing store as active so that it cannot be removed while the HW is
> busy.)
>
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> ---
>  drivers/gpu/drm/i915/gem/i915_gem_clflush.c   |  20 +-
>  drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c    |  18 +-
>  drivers/gpu/drm/i915/gem/i915_gem_domain.c    |  65 ++---
>  .../gpu/drm/i915/gem/i915_gem_execbuffer.c    |  44 +++-
>  drivers/gpu/drm/i915/gem/i915_gem_object.c    |   8 +-
>  drivers/gpu/drm/i915/gem/i915_gem_object.h    |  37 +--
>  .../gpu/drm/i915/gem/i915_gem_object_types.h  |   1 -
>  drivers/gpu/drm/i915/gem/i915_gem_pages.c     | 136 +++++------
>  drivers/gpu/drm/i915/gem/i915_gem_phys.c      |   8 +-
>  drivers/gpu/drm/i915/gem/i915_gem_shrinker.c  |  13 +-
>  drivers/gpu/drm/i915/gem/i915_gem_tiling.c    |   2 -
>  drivers/gpu/drm/i915/gem/i915_gem_userptr.c   |  15 +-
>  .../gpu/drm/i915/gem/selftests/huge_pages.c   |  32 ++-
>  .../i915/gem/selftests/i915_gem_coherency.c   |  14 +-
>  .../drm/i915/gem/selftests/i915_gem_context.c |  10 +-
>  .../drm/i915/gem/selftests/i915_gem_mman.c    |   2 +
>  drivers/gpu/drm/i915/gt/gen6_ppgtt.c          |   2 -
>  drivers/gpu/drm/i915/gt/gen8_ppgtt.c          |   1 -
>  drivers/gpu/drm/i915/gt/intel_ggtt.c          |   5 +-
>  drivers/gpu/drm/i915/gt/intel_gtt.h           |   2 -
>  drivers/gpu/drm/i915/gt/intel_ppgtt.c         |   1 +
>  drivers/gpu/drm/i915/i915_gem.c               |  16 +-
>  drivers/gpu/drm/i915/i915_vma.c               | 225 +++++++-----------
>  drivers/gpu/drm/i915/i915_vma_types.h         |   6 -
>  drivers/gpu/drm/i915/mm/i915_acquire_ctx.c    |  11 +-
>  drivers/gpu/drm/i915/selftests/i915_gem_gtt.c |   4 +-
>  .../drm/i915/selftests/intel_memory_region.c  |  17 +-
>  27 files changed, 319 insertions(+), 396 deletions(-)]

It's trivial to do a search and replace for obj->mm.lock, but that won't solve the locking issue we need to solve with actually using ww_mutex correctly.

We shouldn't return -EAGAIN in userptr if trylock fails, but it should use proper locking. That should be the entire goal of the removal, not just a quick search/replace job. :)

>
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_clflush.c b/drivers/gpu/drm/i915/gem/i915_gem_clflush.c
> index bc0223716906..a32fd0d5570b 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_clflush.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_clflush.c
> @@ -27,16 +27,8 @@ static void __do_clflush(struct drm_i915_gem_object *obj)
>  static int clflush_work(struct dma_fence_work *base)
>  {
>  	struct clflush *clflush = container_of(base, typeof(*clflush), base);
> -	struct drm_i915_gem_object *obj = clflush->obj;
> -	int err;
> -
> -	err = i915_gem_object_pin_pages(obj);
> -	if (err)
> -		return err;
> -
> -	__do_clflush(obj);
> -	i915_gem_object_unpin_pages(obj);
>  
> +	__do_clflush(clflush->obj);
>  	return 0;
>  }
>  
> @@ -44,7 +36,7 @@ static void clflush_release(struct dma_fence_work *base)
>  {
>  	struct clflush *clflush = container_of(base, typeof(*clflush), base);
>  
> -	i915_gem_object_put(clflush->obj);
> +	i915_gem_object_unpin_pages(clflush->obj);
>  }
>  
>  static const struct dma_fence_work_ops clflush_ops = {
> @@ -63,8 +55,14 @@ static struct clflush *clflush_work_create(struct drm_i915_gem_object *obj)
>  	if (!clflush)
>  		return NULL;
>  
> +	if (__i915_gem_object_get_pages_locked(obj)) {
> +		kfree(clflush);
> +		return NULL;
> +	}
> +
>  	dma_fence_work_init(&clflush->base, &clflush_ops);
> -	clflush->obj = i915_gem_object_get(obj); /* obj <-> clflush cycle */
> +	__i915_gem_object_pin_pages(obj);
> +	clflush->obj = obj; /* Beware the obj.resv <-> clflush fence cycle */
>  
>  	return clflush;
>  }
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c
> index 2679380159fc..049a15e6b496 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_dmabuf.c
> @@ -124,19 +124,12 @@ static int i915_gem_begin_cpu_access(struct dma_buf *dma_buf, enum dma_data_dire
>  	bool write = (direction == DMA_BIDIRECTIONAL || direction == DMA_TO_DEVICE);
>  	int err;
>  
> -	err = i915_gem_object_pin_pages(obj);
> -	if (err)
> -		return err;
> -
>  	err = i915_gem_object_lock_interruptible(obj);
>  	if (err)
> -		goto out;
> +		return err;
>  
>  	err = i915_gem_object_set_to_cpu_domain(obj, write);
>  	i915_gem_object_unlock(obj);
> -
> -out:
> -	i915_gem_object_unpin_pages(obj);
>  	return err;
>  }
>  
> @@ -145,19 +138,12 @@ static int i915_gem_end_cpu_access(struct dma_buf *dma_buf, enum dma_data_direct
>  	struct drm_i915_gem_object *obj = dma_buf_to_obj(dma_buf);
>  	int err;
>  
> -	err = i915_gem_object_pin_pages(obj);
> -	if (err)
> -		return err;
> -
>  	err = i915_gem_object_lock_interruptible(obj);
>  	if (err)
> -		goto out;
> +		return err;
>  
>  	err = i915_gem_object_set_to_gtt_domain(obj, false);
>  	i915_gem_object_unlock(obj);
> -
> -out:
> -	i915_gem_object_unpin_pages(obj);
>  	return err;
>  }
>  
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_domain.c b/drivers/gpu/drm/i915/gem/i915_gem_domain.c
> index 7f76fc68f498..30e4b163588b 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_domain.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_domain.c
> @@ -70,7 +70,7 @@ i915_gem_object_set_to_wc_domain(struct drm_i915_gem_object *obj, bool write)
>  	 * continue to assume that the obj remained out of the CPU cached
>  	 * domain.
>  	 */
> -	ret = i915_gem_object_pin_pages(obj);
> +	ret = __i915_gem_object_get_pages_locked(obj);
>  	if (ret)
>  		return ret;
>  
> @@ -94,7 +94,6 @@ i915_gem_object_set_to_wc_domain(struct drm_i915_gem_object *obj, bool write)
>  		obj->mm.dirty = true;
>  	}
>  
> -	i915_gem_object_unpin_pages(obj);
>  	return 0;
>  }
>  
> @@ -131,7 +130,7 @@ i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write)
>  	 * continue to assume that the obj remained out of the CPU cached
>  	 * domain.
>  	 */
> -	ret = i915_gem_object_pin_pages(obj);
> +	ret = __i915_gem_object_get_pages_locked(obj);
>  	if (ret)
>  		return ret;
>  
> @@ -163,7 +162,6 @@ i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write)
>  		spin_unlock(&obj->vma.lock);
>  	}
>  
> -	i915_gem_object_unpin_pages(obj);
>  	return 0;
>  }
>  
> @@ -532,13 +530,9 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data,
>  	 * continue to assume that the obj remained out of the CPU cached
>  	 * domain.
>  	 */
> -	err = i915_gem_object_pin_pages(obj);
> -	if (err)
> -		goto out;
> -
>  	err = i915_gem_object_lock_interruptible(obj);
>  	if (err)
> -		goto out_unpin;
> +		goto out;
>  
>  	if (read_domains & I915_GEM_DOMAIN_WC)
>  		err = i915_gem_object_set_to_wc_domain(obj, write_domain);
> @@ -555,8 +549,6 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data,
>  	if (write_domain)
>  		i915_gem_object_invalidate_frontbuffer(obj, ORIGIN_CPU);
>  
> -out_unpin:
> -	i915_gem_object_unpin_pages(obj);
>  out:
>  	i915_gem_object_put(obj);
>  	return err;
> @@ -572,11 +564,13 @@ int i915_gem_object_prepare_read(struct drm_i915_gem_object *obj,
>  {
>  	int ret;
>  
> +	assert_object_held(obj);
> +
>  	*needs_clflush = 0;
>  	if (!i915_gem_object_has_struct_page(obj))
>  		return -ENODEV;
>  
> -	ret = i915_gem_object_lock_interruptible(obj);
> +	ret = __i915_gem_object_get_pages_locked(obj);
>  	if (ret)
>  		return ret;
>  
> @@ -584,19 +578,11 @@ int i915_gem_object_prepare_read(struct drm_i915_gem_object *obj,
>  				   I915_WAIT_INTERRUPTIBLE,
>  				   MAX_SCHEDULE_TIMEOUT);
>  	if (ret)
> -		goto err_unlock;
> -
> -	ret = i915_gem_object_pin_pages(obj);
> -	if (ret)
> -		goto err_unlock;
> +		return ret;
>  
>  	if (obj->cache_coherent & I915_BO_CACHE_COHERENT_FOR_READ ||
>  	    !static_cpu_has(X86_FEATURE_CLFLUSH)) {
> -		ret = i915_gem_object_set_to_cpu_domain(obj, false);
> -		if (ret)
> -			goto err_unpin;
> -		else
> -			goto out;
> +		return i915_gem_object_set_to_cpu_domain(obj, false);
>  	}
>  
>  	i915_gem_object_flush_write_domain(obj, ~I915_GEM_DOMAIN_CPU);
> @@ -610,15 +596,7 @@ int i915_gem_object_prepare_read(struct drm_i915_gem_object *obj,
>  	    !(obj->read_domains & I915_GEM_DOMAIN_CPU))
>  		*needs_clflush = CLFLUSH_BEFORE;
>  
> -out:
> -	/* return with the pages pinned */
>  	return 0;
> -
> -err_unpin:
> -	i915_gem_object_unpin_pages(obj);
> -err_unlock:
> -	i915_gem_object_unlock(obj);
> -	return ret;
>  }
>  
>  int i915_gem_object_prepare_write(struct drm_i915_gem_object *obj,
> @@ -626,11 +604,13 @@ int i915_gem_object_prepare_write(struct drm_i915_gem_object *obj,
>  {
>  	int ret;
>  
> +	assert_object_held(obj);
> +
>  	*needs_clflush = 0;
>  	if (!i915_gem_object_has_struct_page(obj))
>  		return -ENODEV;
>  
> -	ret = i915_gem_object_lock_interruptible(obj);
> +	ret = __i915_gem_object_get_pages_locked(obj);
>  	if (ret)
>  		return ret;
>  
> @@ -639,20 +619,11 @@ int i915_gem_object_prepare_write(struct drm_i915_gem_object *obj,
>  				   I915_WAIT_ALL,
>  				   MAX_SCHEDULE_TIMEOUT);
>  	if (ret)
> -		goto err_unlock;
> -
> -	ret = i915_gem_object_pin_pages(obj);
> -	if (ret)
> -		goto err_unlock;
> +		return ret;
>  
>  	if (obj->cache_coherent & I915_BO_CACHE_COHERENT_FOR_WRITE ||
> -	    !static_cpu_has(X86_FEATURE_CLFLUSH)) {
> -		ret = i915_gem_object_set_to_cpu_domain(obj, true);
> -		if (ret)
> -			goto err_unpin;
> -		else
> -			goto out;
> -	}
> +	    !static_cpu_has(X86_FEATURE_CLFLUSH))
> +		return i915_gem_object_set_to_cpu_domain(obj, true);
>  
>  	i915_gem_object_flush_write_domain(obj, ~I915_GEM_DOMAIN_CPU);
>  
> @@ -672,15 +643,7 @@ int i915_gem_object_prepare_write(struct drm_i915_gem_object *obj,
>  			*needs_clflush |= CLFLUSH_BEFORE;
>  	}
>  
> -out:
>  	i915_gem_object_invalidate_frontbuffer(obj, ORIGIN_CPU);
>  	obj->mm.dirty = true;
> -	/* return with the pages pinned */
>  	return 0;
> -
> -err_unpin:
> -	i915_gem_object_unpin_pages(obj);
> -err_unlock:
> -	i915_gem_object_unlock(obj);
> -	return ret;
>  }
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> index bcd100f8a6c7..37c0d5058891 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> @@ -963,6 +963,13 @@ static int best_hole(struct drm_mm *mm, struct drm_mm_node *node,
>  	} while (1);
>  }
>  
> +static void eb_pin_vma_pages(struct i915_vma *vma, unsigned int count)
> +{
> +	count = hweight32(count);
> +	while (count--)
> +		__i915_gem_object_pin_pages(vma->obj);
> +}
> +
>  static int eb_reserve_vma(struct eb_vm_work *work, struct eb_bind_vma *bind)
>  {
>  	struct drm_i915_gem_exec_object2 *entry = bind->ev->exec;
> @@ -1074,7 +1081,6 @@ static int eb_reserve_vma(struct eb_vm_work *work, struct eb_bind_vma *bind)
>  		if (unlikely(err))
>  			return err;
>  
> -		atomic_add(I915_VMA_PAGES_ACTIVE, &vma->pages_count);
>  		atomic_or(bind_flags, &vma->flags);
>  
>  		if (i915_vma_is_ggtt(vma))
> @@ -1181,9 +1187,14 @@ static void __eb_bind_vma(struct eb_vm_work *work)
>  		GEM_BUG_ON(vma->vm != vm);
>  		GEM_BUG_ON(!i915_vma_is_active(vma));
>  
> +		if (!vma->pages)
> +			vma->ops->set_pages(vma); /* plain assignment */
> +
>  		vma->ops->bind_vma(vm, &work->stash, vma,
>  				   vma->obj->cache_level, bind->bind_flags);
>  
> +		eb_pin_vma_pages(vma, bind->bind_flags);
> +
>  		if (drm_mm_node_allocated(&bind->hole)) {
>  			mutex_lock(&vm->mutex);
>  			GEM_BUG_ON(bind->hole.mm != &vm->mm);
> @@ -1200,7 +1211,6 @@ static void __eb_bind_vma(struct eb_vm_work *work)
>  
>  put:
>  		GEM_BUG_ON(drm_mm_node_allocated(&bind->hole));
> -		i915_vma_put_pages(vma);
>  	}
>  	work->count = 0;
>  }
> @@ -1302,7 +1312,6 @@ static int eb_prepare_vma(struct eb_vm_work *work,
>  	struct eb_bind_vma *bind = &work->bind[idx];
>  	struct i915_vma *vma = ev->vma;
>  	u64 max_size;
> -	int err;
>  
>  	bind->ev = ev;
>  	bind->hole.flags = 0;
> @@ -1313,11 +1322,24 @@ static int eb_prepare_vma(struct eb_vm_work *work,
>  	if (ev->flags & __EXEC_OBJECT_NEEDS_MAP)
>  		max_size = max_t(u64, max_size, vma->fence_size);
>  
> -	err = i915_vm_alloc_pt_stash(work->vm, &work->stash, max_size);
> -	if (err)
> -		return err;
> +	return i915_vm_alloc_pt_stash(work->vm, &work->stash, max_size);
> +}
>  
> -	return i915_vma_get_pages(vma);
> +static int eb_lock_pt(struct i915_execbuffer *eb,
> +		      struct i915_vm_pt_stash *stash)
> +{
> +	struct i915_page_table *pt;
> +	int n, err;
> +
> +	for (n = 0; n < ARRAY_SIZE(stash->pt); n++) {
> +		for (pt = stash->pt[n]; pt; pt = pt->stash) {
> +			err = i915_acquire_ctx_lock(&eb->acquire, pt->base);
> +			if (err)
> +				return err;
> +		}
> +	}
> +
> +	return 0;
>  }
>  
>  static int wait_for_unbinds(struct i915_execbuffer *eb,
> @@ -1440,11 +1462,11 @@ static int eb_reserve_vm(struct i915_execbuffer *eb)
>  			}
>  		}
>  
> -		err = eb_acquire_mm(eb);
> +		err = eb_lock_pt(eb, &work->stash);
>  		if (err)
>  			return eb_vm_work_cancel(work, err);
>  
> -		err = i915_vm_pin_pt_stash(work->vm, &work->stash);
> +		err = eb_acquire_mm(eb);
>  		if (err)
>  			return eb_vm_work_cancel(work, err);
>  
> @@ -2657,7 +2679,7 @@ static int eb_parse_pipeline(struct i915_execbuffer *eb,
>  	if (!pw)
>  		return -ENOMEM;
>  
> -	ptr = i915_gem_object_pin_map(shadow->obj, I915_MAP_FORCE_WB);
> +	ptr = __i915_gem_object_pin_map_locked(shadow->obj, I915_MAP_FORCE_WB);
>  	if (IS_ERR(ptr)) {
>  		err = PTR_ERR(ptr);
>  		goto err_free;
> @@ -2665,7 +2687,7 @@ static int eb_parse_pipeline(struct i915_execbuffer *eb,
>  
>  	if (!(batch->obj->cache_coherent & I915_BO_CACHE_COHERENT_FOR_READ) &&
>  	    i915_has_memcpy_from_wc()) {
> -		ptr = i915_gem_object_pin_map(batch->obj, I915_MAP_WC);
> +		ptr = __i915_gem_object_pin_map_locked(batch->obj, I915_MAP_WC);
>  		if (IS_ERR(ptr)) {
>  			err = PTR_ERR(ptr);
>  			goto err_dst;
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.c b/drivers/gpu/drm/i915/gem/i915_gem_object.c
> index c8421fd9d2dc..799ad4e648aa 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_object.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_object.c
> @@ -53,8 +53,6 @@ void i915_gem_object_init(struct drm_i915_gem_object *obj,
>  			  const struct drm_i915_gem_object_ops *ops,
>  			  struct lock_class_key *key)
>  {
> -	__mutex_init(&obj->mm.lock, ops->name ?: "obj->mm.lock", key);
> -
>  	spin_lock_init(&obj->vma.lock);
>  	INIT_LIST_HEAD(&obj->vma.list);
>  
> @@ -73,10 +71,6 @@ void i915_gem_object_init(struct drm_i915_gem_object *obj,
>  	obj->mm.madv = I915_MADV_WILLNEED;
>  	INIT_RADIX_TREE(&obj->mm.get_page.radix, GFP_KERNEL | __GFP_NOWARN);
>  	mutex_init(&obj->mm.get_page.lock);
> -
> -	if (IS_ENABLED(CONFIG_LOCKDEP) && i915_gem_object_is_shrinkable(obj))
> -		i915_gem_shrinker_taints_mutex(to_i915(obj->base.dev),
> -					       &obj->mm.lock);
>  }
>  
>  /**
> @@ -229,10 +223,12 @@ static void __i915_gem_free_objects(struct drm_i915_private *i915,
>  
>  		GEM_BUG_ON(!list_empty(&obj->lut_list));
>  
> +		i915_gem_object_lock(obj);
>  		atomic_set(&obj->mm.pages_pin_count, 0);
>  		__i915_gem_object_put_pages(obj);
>  		GEM_BUG_ON(i915_gem_object_has_pages(obj));
>  		bitmap_free(obj->bit_17);
> +		i915_gem_object_unlock(obj);
>  
>  		if (obj->base.import_attach)
>  			drm_prime_gem_destroy(&obj->base, NULL);
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.h b/drivers/gpu/drm/i915/gem/i915_gem_object.h
> index 25714bf70b6a..8f1d20f6d42a 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_object.h
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_object.h
> @@ -275,36 +275,9 @@ void __i915_gem_object_set_pages(struct drm_i915_gem_object *obj,
>  				 struct sg_table *pages,
>  				 unsigned int sg_page_sizes);
>  
> -int ____i915_gem_object_get_pages(struct drm_i915_gem_object *obj);
> -int __i915_gem_object_get_pages(struct drm_i915_gem_object *obj);
> -
> -enum i915_mm_subclass { /* lockdep subclass for obj->mm.lock/struct_mutex */
> -	I915_MM_NORMAL = 0,
> -	/*
> -	 * Only used by struct_mutex, when called "recursively" from
> -	 * direct-reclaim-esque. Safe because there is only every one
> -	 * struct_mutex in the entire system.
> -	 */
> -	I915_MM_SHRINKER = 1,
> -	/*
> -	 * Used for obj->mm.lock when allocating pages. Safe because the object
> -	 * isn't yet on any LRU, and therefore the shrinker can't deadlock on
> -	 * it. As soon as the object has pages, obj->mm.lock nests within
> -	 * fs_reclaim.
> -	 */
> -	I915_MM_GET_PAGES = 1,
> -};
> -
> -static inline int __must_check
> -i915_gem_object_pin_pages(struct drm_i915_gem_object *obj)
> -{
> -	might_lock_nested(&obj->mm.lock, I915_MM_GET_PAGES);
> +int __i915_gem_object_get_pages_locked(struct drm_i915_gem_object *obj);
>  
> -	if (atomic_inc_not_zero(&obj->mm.pages_pin_count))
> -		return 0;
> -
> -	return __i915_gem_object_get_pages(obj);
> -}
> +int i915_gem_object_pin_pages(struct drm_i915_gem_object *obj);
>  
>  static inline bool
>  i915_gem_object_has_pages(struct drm_i915_gem_object *obj)
> @@ -372,6 +345,9 @@ enum i915_map_type {
>  void *__must_check i915_gem_object_pin_map(struct drm_i915_gem_object *obj,
>  					   enum i915_map_type type);
>  
> +void *__i915_gem_object_pin_map_locked(struct drm_i915_gem_object *obj,
> +				       enum i915_map_type type);
> +
>  static inline void *__i915_gem_object_mapping(struct drm_i915_gem_object *obj)
>  {
>  	return page_mask_bits(obj->mm.mapping);
> @@ -419,8 +395,7 @@ int i915_gem_object_prepare_write(struct drm_i915_gem_object *obj,
>  static inline void
>  i915_gem_object_finish_access(struct drm_i915_gem_object *obj)
>  {
> -	i915_gem_object_unpin_pages(obj);
> -	i915_gem_object_unlock(obj);
> +	assert_object_held(obj);
>  }
>  
>  static inline struct intel_engine_cs *
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object_types.h b/drivers/gpu/drm/i915/gem/i915_gem_object_types.h
> index d0847d7896f9..ae3303ba272c 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_object_types.h
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_object_types.h
> @@ -187,7 +187,6 @@ struct drm_i915_gem_object {
>  		 * Protects the pages and their use. Do not use directly, but
>  		 * instead go through the pin/unpin interfaces.
>  		 */
> -		struct mutex lock;
>  		atomic_t pages_pin_count;
>  		atomic_t shrink_pin;
>  
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_pages.c b/drivers/gpu/drm/i915/gem/i915_gem_pages.c
> index af9e48ee4a33..7799f36b4b5d 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_pages.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_pages.c
> @@ -18,7 +18,7 @@ void __i915_gem_object_set_pages(struct drm_i915_gem_object *obj,
>  	unsigned long supported = INTEL_INFO(i915)->page_sizes;
>  	int i;
>  
> -	lockdep_assert_held(&obj->mm.lock);
> +	assert_object_held(obj);
>  
>  	if (i915_gem_object_is_volatile(obj))
>  		obj->mm.madv = I915_MADV_DONTNEED;
> @@ -81,13 +81,19 @@ void __i915_gem_object_set_pages(struct drm_i915_gem_object *obj,
>  	}
>  }
>  
> -int ____i915_gem_object_get_pages(struct drm_i915_gem_object *obj)
> +int __i915_gem_object_get_pages_locked(struct drm_i915_gem_object *obj)
>  {
> -	struct drm_i915_private *i915 = to_i915(obj->base.dev);
>  	int err;
>  
> +	assert_object_held(obj);
> +
> +	if (i915_gem_object_has_pages(obj))
> +		return 0;
> +
> +	GEM_BUG_ON(i915_gem_object_has_pinned_pages(obj));
> +
>  	if (unlikely(obj->mm.madv != I915_MADV_WILLNEED)) {
> -		drm_dbg(&i915->drm,
> +		drm_dbg(obj->base.dev,
>  			"Attempting to obtain a purgeable object\n");
>  		return -EFAULT;
>  	}
> @@ -98,34 +104,33 @@ int ____i915_gem_object_get_pages(struct drm_i915_gem_object *obj)
>  	return err;
>  }
>  
> -/* Ensure that the associated pages are gathered from the backing storage
> +/*
> + * Ensure that the associated pages are gathered from the backing storage
>   * and pinned into our object. i915_gem_object_pin_pages() may be called
>   * multiple times before they are released by a single call to
>   * i915_gem_object_unpin_pages() - once the pages are no longer referenced
>   * either as a result of memory pressure (reaping pages under the shrinker)
>   * or as the object is itself released.
>   */
> -int __i915_gem_object_get_pages(struct drm_i915_gem_object *obj)
> +int i915_gem_object_pin_pages(struct drm_i915_gem_object *obj)
>  {
>  	int err;
>  
> -	err = mutex_lock_interruptible_nested(&obj->mm.lock, I915_MM_GET_PAGES);
> +	might_lock(&obj->base.resv->lock.base);
> +
> +	if (atomic_inc_not_zero(&obj->mm.pages_pin_count))
> +		return 0;
> +
> +	err = i915_gem_object_lock_interruptible(obj);
>  	if (err)
>  		return err;
>  
> -	if (unlikely(!i915_gem_object_has_pages(obj))) {
> -		GEM_BUG_ON(i915_gem_object_has_pinned_pages(obj));
> -
> -		err = ____i915_gem_object_get_pages(obj);
> -		if (err)
> -			goto unlock;
> +	err = __i915_gem_object_get_pages_locked(obj);
> +	if (err == 0)
> +		atomic_inc(&obj->mm.pages_pin_count);
>  
> -		smp_mb__before_atomic();
> -	}
> -	atomic_inc(&obj->mm.pages_pin_count);
> +	i915_gem_object_unlock(obj);
>  
> -unlock:
> -	mutex_unlock(&obj->mm.lock);
>  	return err;
>  }
>  
> @@ -140,7 +145,7 @@ void i915_gem_object_truncate(struct drm_i915_gem_object *obj)
>  /* Try to discard unwanted pages */
>  void i915_gem_object_writeback(struct drm_i915_gem_object *obj)
>  {
> -	lockdep_assert_held(&obj->mm.lock);
> +	assert_object_held(obj);
>  	GEM_BUG_ON(i915_gem_object_has_pages(obj));
>  
>  	if (obj->ops->writeback)
> @@ -194,17 +199,15 @@ __i915_gem_object_unset_pages(struct drm_i915_gem_object *obj)
>  int __i915_gem_object_put_pages(struct drm_i915_gem_object *obj)
>  {
>  	struct sg_table *pages;
> -	int err;
> +
> +	/* May be called by shrinker from within get_pages() (on another bo) */
> +	assert_object_held(obj);
>  
>  	if (i915_gem_object_has_pinned_pages(obj))
>  		return -EBUSY;
>  
> -	/* May be called by shrinker from within get_pages() (on another bo) */
> -	mutex_lock(&obj->mm.lock);
> -	if (unlikely(atomic_read(&obj->mm.pages_pin_count))) {
> -		err = -EBUSY;
> -		goto unlock;
> -	}
> +	if (unlikely(atomic_read(&obj->mm.pages_pin_count)))
> +		return -EBUSY;
>  
>  	i915_gem_object_release_mmap_offset(obj);
>  
> @@ -227,11 +230,7 @@ int __i915_gem_object_put_pages(struct drm_i915_gem_object *obj)
>  	if (!IS_ERR(pages))
>  		obj->ops->put_pages(obj, pages);
>  
> -	err = 0;
> -unlock:
> -	mutex_unlock(&obj->mm.lock);
> -
> -	return err;
> +	return 0;
>  }
>  
>  static inline pte_t iomap_pte(resource_size_t base,
> @@ -311,48 +310,28 @@ static void *i915_gem_object_map(struct drm_i915_gem_object *obj,
>  	return area->addr;
>  }
>  
> -/* get, pin, and map the pages of the object into kernel space */
> -void *i915_gem_object_pin_map(struct drm_i915_gem_object *obj,
> -			      enum i915_map_type type)
> +void *__i915_gem_object_pin_map_locked(struct drm_i915_gem_object *obj,
> +				       enum i915_map_type type)
>  {
>  	enum i915_map_type has_type;
>  	unsigned int flags;
>  	bool pinned;
>  	void *ptr;
> -	int err;
> +
> +	assert_object_held(obj);
> +	GEM_BUG_ON(!i915_gem_object_has_pages(obj));
>  
>  	flags = I915_GEM_OBJECT_HAS_STRUCT_PAGE | I915_GEM_OBJECT_HAS_IOMEM;
>  	if (!i915_gem_object_type_has(obj, flags))
>  		return ERR_PTR(-ENXIO);
>  
> -	err = mutex_lock_interruptible_nested(&obj->mm.lock, I915_MM_GET_PAGES);
> -	if (err)
> -		return ERR_PTR(err);
> -
>  	pinned = !(type & I915_MAP_OVERRIDE);
>  	type &= ~I915_MAP_OVERRIDE;
>  
> -	if (!atomic_inc_not_zero(&obj->mm.pages_pin_count)) {
> -		if (unlikely(!i915_gem_object_has_pages(obj))) {
> -			GEM_BUG_ON(i915_gem_object_has_pinned_pages(obj));
> -
> -			err = ____i915_gem_object_get_pages(obj);
> -			if (err)
> -				goto err_unlock;
> -
> -			smp_mb__before_atomic();
> -		}
> -		atomic_inc(&obj->mm.pages_pin_count);
> -		pinned = false;
> -	}
> -	GEM_BUG_ON(!i915_gem_object_has_pages(obj));
> -
>  	ptr = page_unpack_bits(obj->mm.mapping, &has_type);
>  	if (ptr && has_type != type) {
> -		if (pinned) {
> -			err = -EBUSY;
> -			goto err_unpin;
> -		}
> +		if (pinned)
> +			return ERR_PTR(-EBUSY);
>  
>  		unmap_object(obj, ptr);
>  
> @@ -361,23 +340,38 @@ void *i915_gem_object_pin_map(struct drm_i915_gem_object *obj,
>  
>  	if (!ptr) {
>  		ptr = i915_gem_object_map(obj, type);
> -		if (!ptr) {
> -			err = -ENOMEM;
> -			goto err_unpin;
> -		}
> +		if (!ptr)
> +			return ERR_PTR(-ENOMEM);
>  
>  		obj->mm.mapping = page_pack_bits(ptr, type);
>  	}
>  
> -out_unlock:
> -	mutex_unlock(&obj->mm.lock);
> +	__i915_gem_object_pin_pages(obj);
>  	return ptr;
> +}
> +
> +/* get, pin, and map the pages of the object into kernel space */
> +void *i915_gem_object_pin_map(struct drm_i915_gem_object *obj,
> +			      enum i915_map_type type)
> +{
> +	void *ptr;
> +	int err;
> +
> +	err = i915_gem_object_lock_interruptible(obj);
> +	if (err)
> +		return ERR_PTR(err);
>  
> -err_unpin:
> -	atomic_dec(&obj->mm.pages_pin_count);
> -err_unlock:
> -	ptr = ERR_PTR(err);
> -	goto out_unlock;
> +	err = __i915_gem_object_get_pages_locked(obj);
> +	if (err) {
> +		ptr = ERR_PTR(err);
> +		goto out;
> +	}
> +
> +	ptr = __i915_gem_object_pin_map_locked(obj, type);
> +
> +out:
> +	i915_gem_object_unlock(obj);
> +	return ptr;
>  }
>  
>  void __i915_gem_object_flush_map(struct drm_i915_gem_object *obj,
> @@ -419,7 +413,9 @@ i915_gem_object_get_sg(struct drm_i915_gem_object *obj,
>  
>  	might_sleep();
>  	GEM_BUG_ON(n >= obj->base.size >> PAGE_SHIFT);
> -	GEM_BUG_ON(!i915_gem_object_has_pinned_pages(obj));
> +	GEM_BUG_ON(!i915_gem_object_has_pages(obj));
> +	GEM_BUG_ON(!mutex_is_locked(&obj->base.resv->lock.base) &&
> +		   !i915_gem_object_has_pinned_pages(obj));
>  
>  	/* As we iterate forward through the sg, we record each entry in a
>  	 * radixtree for quick repeated (backwards) lookups. If we have seen
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_phys.c b/drivers/gpu/drm/i915/gem/i915_gem_phys.c
> index 28147aab47b9..f7f93b68b7c1 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_phys.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_phys.c
> @@ -165,7 +165,7 @@ int i915_gem_object_attach_phys(struct drm_i915_gem_object *obj, int align)
>  	if (err)
>  		return err;
>  
> -	mutex_lock_nested(&obj->mm.lock, I915_MM_GET_PAGES);
> +	i915_gem_object_lock(obj);
>  
>  	if (obj->mm.madv != I915_MADV_WILLNEED) {
>  		err = -EFAULT;
> @@ -186,7 +186,7 @@ int i915_gem_object_attach_phys(struct drm_i915_gem_object *obj, int align)
>  
>  	obj->ops = &i915_gem_phys_ops;
>  
> -	err = ____i915_gem_object_get_pages(obj);
> +	err = __i915_gem_object_get_pages_locked(obj);
>  	if (err)
>  		goto err_xfer;
>  
> @@ -198,7 +198,7 @@ int i915_gem_object_attach_phys(struct drm_i915_gem_object *obj, int align)
>  
>  	i915_gem_object_release_memory_region(obj);
>  
> -	mutex_unlock(&obj->mm.lock);
> +	i915_gem_object_unlock(obj);
>  	return 0;
>  
>  err_xfer:
> @@ -209,7 +209,7 @@ int i915_gem_object_attach_phys(struct drm_i915_gem_object *obj, int align)
>  		__i915_gem_object_set_pages(obj, pages, sg_page_sizes);
>  	}
>  err_unlock:
> -	mutex_unlock(&obj->mm.lock);
> +	i915_gem_object_unlock(obj);
>  	return err;
>  }
>  
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_shrinker.c b/drivers/gpu/drm/i915/gem/i915_gem_shrinker.c
> index 1ced1e5d2ec0..a2713b251d70 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_shrinker.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_shrinker.c
> @@ -45,10 +45,7 @@ static bool unsafe_drop_pages(struct drm_i915_gem_object *obj,
>  	if (!(shrink & I915_SHRINK_BOUND))
>  		flags = I915_GEM_OBJECT_UNBIND_TEST;
>  
> -	if (i915_gem_object_unbind(obj, flags) == 0)
> -		__i915_gem_object_put_pages(obj);
> -
> -	return !i915_gem_object_has_pages(obj);
> +	return i915_gem_object_unbind(obj, flags) == 0;
>  }
>  
>  static void try_to_writeback(struct drm_i915_gem_object *obj,
> @@ -192,14 +189,14 @@ i915_gem_shrink(struct drm_i915_private *i915,
>  
>  			spin_unlock_irqrestore(&i915->mm.obj_lock, flags);
>  
> -			if (unsafe_drop_pages(obj, shrink)) {
> -				/* May arrive from get_pages on another bo */
> -				mutex_lock(&obj->mm.lock);
> +			if (unsafe_drop_pages(obj, shrink) &&
> +			    i915_gem_object_trylock(obj)) {
> +				__i915_gem_object_put_pages(obj);
>  				if (!i915_gem_object_has_pages(obj)) {
>  					try_to_writeback(obj, shrink);
>  					count += obj->base.size >> PAGE_SHIFT;
>  				}
> -				mutex_unlock(&obj->mm.lock);
> +				i915_gem_object_unlock(obj);
>  			}
>  
>  			scanned += obj->base.size >> PAGE_SHIFT;
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_tiling.c b/drivers/gpu/drm/i915/gem/i915_gem_tiling.c
> index ff72ee2fd9cd..ac12e1c20e66 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_tiling.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_tiling.c
> @@ -265,7 +265,6 @@ i915_gem_object_set_tiling(struct drm_i915_gem_object *obj,
>  	 * pages to prevent them being swapped out and causing corruption
>  	 * due to the change in swizzling.
>  	 */
> -	mutex_lock(&obj->mm.lock);
>  	if (i915_gem_object_has_pages(obj) &&
>  	    obj->mm.madv == I915_MADV_WILLNEED &&
>  	    i915->quirks & QUIRK_PIN_SWIZZLED_PAGES) {
> @@ -280,7 +279,6 @@ i915_gem_object_set_tiling(struct drm_i915_gem_object *obj,
>  			obj->mm.quirked = true;
>  		}
>  	}
> -	mutex_unlock(&obj->mm.lock);
>  
>  	spin_lock(&obj->vma.lock);
>  	for_each_ggtt_vma(vma, obj) {
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_userptr.c b/drivers/gpu/drm/i915/gem/i915_gem_userptr.c
> index e946032b13e4..80907c00c6fd 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_userptr.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_userptr.c
> @@ -129,8 +129,15 @@ userptr_mn_invalidate_range_start(struct mmu_notifier *_mn,
>  		ret = i915_gem_object_unbind(obj,
>  					     I915_GEM_OBJECT_UNBIND_ACTIVE |
>  					     I915_GEM_OBJECT_UNBIND_BARRIER);
> -		if (ret == 0)
> -			ret = __i915_gem_object_put_pages(obj);
> +		if (ret == 0) {
> +			/* ww_mutex and mmu_notifier is fs_reclaim tainted */
> +			if (i915_gem_object_trylock(obj)) {
> +				ret = __i915_gem_object_put_pages(obj);
> +				i915_gem_object_unlock(obj);
> +			} else {
> +				ret = -EAGAIN;
> +			}
> +		}
>  		i915_gem_object_put(obj);
>  		if (ret)
>  			return ret;

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Intel-gfx] [PATCH 10/20] drm/i915: Export a preallocate variant of i915_active_acquire()
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 10/20] drm/i915: Export a preallocate variant of i915_active_acquire() Chris Wilson
@ 2020-07-09 14:36   ` Maarten Lankhorst
  2020-07-10 12:24     ` Tvrtko Ursulin
  2020-07-13 14:29   ` Tvrtko Ursulin
  1 sibling, 1 reply; 65+ messages in thread
From: Maarten Lankhorst @ 2020-07-09 14:36 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx

Op 06-07-2020 om 08:19 schreef Chris Wilson:
> Sometimes we have to be very careful not to allocate underneath a mutex
> (or spinlock) and yet still want to track activity. Enter
> i915_active_acquire_for_context(). This raises the activity counter on
> i915_active prior to use and ensures that the fence-tree contains a slot
> for the context.
>
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> ---
>  .../gpu/drm/i915/gem/i915_gem_execbuffer.c    |   2 +-
>  drivers/gpu/drm/i915/gt/intel_timeline.c      |   4 +-
>  drivers/gpu/drm/i915/i915_active.c            | 113 +++++++++++++++---
>  drivers/gpu/drm/i915/i915_active.h            |  14 ++-
>  4 files changed, 113 insertions(+), 20 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> index 1015c4fd9f3e..6d20be29ff3c 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> @@ -1789,7 +1789,7 @@ __parser_mark_active(struct i915_vma *vma,
>  {
>  	struct intel_gt_buffer_pool_node *node = vma->private;
>  
> -	return i915_active_ref(&node->active, tl, fence);
> +	return i915_active_ref(&node->active, tl->fence_context, fence);
>  }
>  
>  static int
> diff --git a/drivers/gpu/drm/i915/gt/intel_timeline.c b/drivers/gpu/drm/i915/gt/intel_timeline.c
> index 4546284fede1..e4a5326633b8 100644
> --- a/drivers/gpu/drm/i915/gt/intel_timeline.c
> +++ b/drivers/gpu/drm/i915/gt/intel_timeline.c
> @@ -479,7 +479,9 @@ __intel_timeline_get_seqno(struct intel_timeline *tl,
>  	 * free it after the current request is retired, which ensures that
>  	 * all writes into the cacheline from previous requests are complete.
>  	 */
> -	err = i915_active_ref(&tl->hwsp_cacheline->active, tl, &rq->fence);
> +	err = i915_active_ref(&tl->hwsp_cacheline->active,
> +			      tl->fence_context,
> +			      &rq->fence);
>  	if (err)
>  		goto err_cacheline;
>  
> diff --git a/drivers/gpu/drm/i915/i915_active.c b/drivers/gpu/drm/i915/i915_active.c
> index d960d0be5bd2..3f595446fd44 100644
> --- a/drivers/gpu/drm/i915/i915_active.c
> +++ b/drivers/gpu/drm/i915/i915_active.c
> @@ -217,11 +217,10 @@ excl_retire(struct dma_fence *fence, struct dma_fence_cb *cb)
>  }
>  
>  static struct i915_active_fence *
> -active_instance(struct i915_active *ref, struct intel_timeline *tl)
> +active_instance(struct i915_active *ref, u64 idx)
>  {
>  	struct active_node *node, *prealloc;
>  	struct rb_node **p, *parent;
> -	u64 idx = tl->fence_context;
>  
>  	/*
>  	 * We track the most recently used timeline to skip a rbtree search
> @@ -353,21 +352,17 @@ __active_del_barrier(struct i915_active *ref, struct active_node *node)
>  	return ____active_del_barrier(ref, node, barrier_to_engine(node));
>  }
>  
> -int i915_active_ref(struct i915_active *ref,
> -		    struct intel_timeline *tl,
> -		    struct dma_fence *fence)
> +int i915_active_ref(struct i915_active *ref, u64 idx, struct dma_fence *fence)
>  {
>  	struct i915_active_fence *active;
>  	int err;
>  
> -	lockdep_assert_held(&tl->mutex);
> -
>  	/* Prevent reaping in case we malloc/wait while building the tree */
>  	err = i915_active_acquire(ref);
>  	if (err)
>  		return err;
>  
> -	active = active_instance(ref, tl);
> +	active = active_instance(ref, idx);
>  	if (!active) {
>  		err = -ENOMEM;
>  		goto out;
> @@ -384,32 +379,104 @@ int i915_active_ref(struct i915_active *ref,
>  		atomic_dec(&ref->count);
>  	}
>  	if (!__i915_active_fence_set(active, fence))
> -		atomic_inc(&ref->count);
> +		__i915_active_acquire(ref);
>  
>  out:
>  	i915_active_release(ref);
>  	return err;
>  }
>  
> -struct dma_fence *
> -i915_active_set_exclusive(struct i915_active *ref, struct dma_fence *f)
> +static struct dma_fence *
> +__i915_active_set_fence(struct i915_active *ref,
> +			struct i915_active_fence *active,
> +			struct dma_fence *fence)
>  {
>  	struct dma_fence *prev;
>  
>  	/* We expect the caller to manage the exclusive timeline ordering */
>  	GEM_BUG_ON(i915_active_is_idle(ref));
>  
> +	if (is_barrier(active)) { /* proto-node used by our idle barrier */
> +		/*
> +		 * This request is on the kernel_context timeline, and so
> +		 * we can use it to substitute for the pending idle-barrer
> +		 * request that we want to emit on the kernel_context.
> +		 */
> +		__active_del_barrier(ref, node_from_active(active));
> +		RCU_INIT_POINTER(active->fence, NULL);
> +		atomic_dec(&ref->count);
> +	}
> +
>  	rcu_read_lock();
> -	prev = __i915_active_fence_set(&ref->excl, f);
> +	prev = __i915_active_fence_set(active, fence);
>  	if (prev)
>  		prev = dma_fence_get_rcu(prev);
>  	else
> -		atomic_inc(&ref->count);
> +		__i915_active_acquire(ref);
>  	rcu_read_unlock();
>  
>  	return prev;
>  }
>  
> +static struct i915_active_fence *
> +__active_lookup(struct i915_active *ref, u64 idx)
> +{
> +	struct active_node *node;
> +	struct rb_node *p;
> +
> +	/* Like active_instance() but with no malloc */
> +
> +	node = READ_ONCE(ref->cache);
> +	if (node && node->timeline == idx)
> +		return &node->base;
> +
> +	spin_lock_irq(&ref->tree_lock);
> +	GEM_BUG_ON(i915_active_is_idle(ref));
> +
> +	p = ref->tree.rb_node;
> +	while (p) {
> +		node = rb_entry(p, struct active_node, node);
> +		if (node->timeline == idx) {
> +			ref->cache = node;
> +			spin_unlock_irq(&ref->tree_lock);
> +			return &node->base;
> +		}
> +
> +		if (node->timeline < idx)
> +			p = p->rb_right;
> +		else
> +			p = p->rb_left;
> +	}
> +
> +	spin_unlock_irq(&ref->tree_lock);
> +
> +	return NULL;
> +}
> +
> +struct dma_fence *
> +__i915_active_ref(struct i915_active *ref, u64 idx, struct dma_fence *fence)
> +{
> +	struct dma_fence *prev = ERR_PTR(-ENOENT);
> +	struct i915_active_fence *active;
> +
> +	if (!i915_active_acquire_if_busy(ref))
> +		return ERR_PTR(-EINVAL);
> +
> +	active = __active_lookup(ref, idx);
> +	if (active)
> +		prev = __i915_active_set_fence(ref, active, fence);
> +
> +	i915_active_release(ref);
> +	return prev;
> +}
> +
> +struct dma_fence *
> +i915_active_set_exclusive(struct i915_active *ref, struct dma_fence *f)
> +{
> +	/* We expect the caller to manage the exclusive timeline ordering */
> +	return __i915_active_set_fence(ref, &ref->excl, f);
> +}
> +
>  bool i915_active_acquire_if_busy(struct i915_active *ref)
>  {
>  	debug_active_assert(ref);
> @@ -443,6 +510,24 @@ int i915_active_acquire(struct i915_active *ref)
>  	return err;
>  }
>  
> +int i915_active_acquire_for_context(struct i915_active *ref, u64 idx)
> +{
> +	struct i915_active_fence *active;
> +	int err;
> +
> +	err = i915_active_acquire(ref);
> +	if (err)
> +		return err;
> +
> +	active = active_instance(ref, idx);
> +	if (!active) {
> +		i915_active_release(ref);
> +		return -ENOMEM;
> +	}
> +
> +	return 0; /* return with active ref */
> +}
> +
>  void i915_active_release(struct i915_active *ref)
>  {
>  	debug_active_assert(ref);
> @@ -804,7 +889,7 @@ int i915_active_acquire_preallocate_barrier(struct i915_active *ref,
>  			 */
>  			RCU_INIT_POINTER(node->base.fence, ERR_PTR(-EAGAIN));
>  			node->base.cb.node.prev = (void *)engine;
> -			atomic_inc(&ref->count);
> +			__i915_active_acquire(ref);
>  		}
>  		GEM_BUG_ON(rcu_access_pointer(node->base.fence) != ERR_PTR(-EAGAIN));
>  
> diff --git a/drivers/gpu/drm/i915/i915_active.h b/drivers/gpu/drm/i915/i915_active.h
> index cf4058150966..2e0bcb3289ec 100644
> --- a/drivers/gpu/drm/i915/i915_active.h
> +++ b/drivers/gpu/drm/i915/i915_active.h
> @@ -163,14 +163,18 @@ void __i915_active_init(struct i915_active *ref,
>  	__i915_active_init(ref, active, retire, &__mkey, &__wkey);	\
>  } while (0)
>  
> -int i915_active_ref(struct i915_active *ref,
> -		    struct intel_timeline *tl,
> -		    struct dma_fence *fence);
> +struct dma_fence *
> +__i915_active_ref(struct i915_active *ref, u64 idx, struct dma_fence *fence);
> +int i915_active_ref(struct i915_active *ref, u64 idx, struct dma_fence *fence);
>  
>  static inline int
>  i915_active_add_request(struct i915_active *ref, struct i915_request *rq)
>  {
> -	return i915_active_ref(ref, i915_request_timeline(rq), &rq->fence);
> +	struct intel_timeline *tl = i915_request_timeline(rq);
> +
> +	lockdep_assert_held(&tl->mutex);
> +
> +	return i915_active_ref(ref, tl->fence_context, &rq->fence);
>  }
>  
>  struct dma_fence *
> @@ -198,7 +202,9 @@ int i915_request_await_active(struct i915_request *rq,
>  #define I915_ACTIVE_AWAIT_BARRIER BIT(2)
>  
>  int i915_active_acquire(struct i915_active *ref);
> +int i915_active_acquire_for_context(struct i915_active *ref, u64 idx);
>  bool i915_active_acquire_if_busy(struct i915_active *ref);
> +
>  void i915_active_release(struct i915_active *ref);
>  
>  static inline void __i915_active_acquire(struct i915_active *ref)

Somewhere this is hiding a locking inversion in the later part of this series.

tl->mutex needs to be an inner lock of ww objects, see my patch series.

Since this is not the case, you're somehow hiding a locking inversion.

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Intel-gfx] [PATCH 16/20] drm/i915/gem: Reintroduce multiple passes for reloc processing
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 16/20] drm/i915/gem: Reintroduce multiple passes for reloc processing Chris Wilson
@ 2020-07-09 15:39   ` Tvrtko Ursulin
  0 siblings, 0 replies; 65+ messages in thread
From: Tvrtko Ursulin @ 2020-07-09 15:39 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 06/07/2020 07:19, Chris Wilson wrote:
> The prospect of locking the entire submission sequence under a wide
> ww_mutex re-imposes some key restrictions, in particular that we must
> not call copy_(from|to)_user underneath the mutex (as the faulthandlers
> themselves may need to take the ww_mutex). To satisfy this requirement,
> we need to split the relocation handling into multiple phases again.
> After dropping the reservations, we need to allocate enough buffer space
> to both copy the relocations from userspace into, and serve as the
> relocation command buffer. Once we have finished copying the
> relocations, we can then re-aquire all the objects for the execbuf and
> rebind them, including our new relocations objects. After we have bound
> all the new and old objects into their final locations, we can then
> convert the relocation entries into the GPU commands to update the
> relocated vma. Finally, once it is all over and we have dropped the
> ww_mutex for the last time, we can then complete the update of the user
> relocation entries.

Good text. :)

> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> ---
>   .../gpu/drm/i915/gem/i915_gem_execbuffer.c    | 842 ++++++++----------
>   .../i915/gem/selftests/i915_gem_execbuffer.c  | 195 ++--
>   2 files changed, 520 insertions(+), 517 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> index 629b736adf2c..fbf5c5cd51ca 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> @@ -35,6 +35,7 @@ struct eb_vma {
>   
>   	/** This vma's place in the execbuf reservation list */
>   	struct drm_i915_gem_exec_object2 *exec;
> +	u32 bias;
>   
>   	struct list_head bind_link;
>   	struct list_head unbound_link;
> @@ -60,15 +61,12 @@ struct eb_vma_array {
>   #define __EXEC_OBJECT_HAS_PIN		BIT(31)
>   #define __EXEC_OBJECT_HAS_FENCE		BIT(30)
>   #define __EXEC_OBJECT_NEEDS_MAP		BIT(29)
> -#define __EXEC_OBJECT_NEEDS_BIAS	BIT(28)
> -#define __EXEC_OBJECT_INTERNAL_FLAGS	(~0u << 28) /* all of the above */
> +#define __EXEC_OBJECT_INTERNAL_FLAGS	(~0u << 29) /* all of the above */
>   
>   #define __EXEC_HAS_RELOC	BIT(31)
>   #define __EXEC_INTERNAL_FLAGS	(~0u << 31)
>   #define UPDATE			PIN_OFFSET_FIXED
>   
> -#define BATCH_OFFSET_BIAS (256*1024)
> -
>   #define __I915_EXEC_ILLEGAL_FLAGS \
>   	(__I915_EXEC_UNKNOWN_FLAGS | \
>   	 I915_EXEC_CONSTANTS_MASK  | \
> @@ -266,20 +264,18 @@ struct i915_execbuffer {
>   	 * obj/page
>   	 */
>   	struct reloc_cache {
> -		struct drm_mm_node node; /** temporary GTT binding */
>   		unsigned int gen; /** Cached value of INTEL_GEN */
>   		bool use_64bit_reloc : 1;
> -		bool has_llc : 1;
>   		bool has_fence : 1;
>   		bool needs_unfenced : 1;
>   
>   		struct intel_context *ce;
>   
> -		struct i915_vma *target;
> -		struct i915_request *rq;
> -		struct i915_vma *rq_vma;
> -		u32 *rq_cmd;
> -		unsigned int rq_size;
> +		struct eb_relocs_link {
> +			struct i915_vma *vma;
> +		} head;
> +		struct drm_i915_gem_relocation_entry *map;
> +		unsigned int pos;

It's not trivial so please add some commentary around the new struct 
members. List handling (is there a single linked list in kernel which 
could be used for clarity?). Or maybe it is not a list.. Why is a list 
of vma links inside a mapped GEM bo? What are pos, bufsz, later max, 
etc. So a bit of high level operation and a bit of per field. I think 
it's needed because it is not straightforward.

Regards,

Tvrtko
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Intel-gfx] [PATCH 04/20] drm/i915/gem: Rename execbuf.bind_link to unbound_link
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 04/20] drm/i915/gem: Rename execbuf.bind_link to unbound_link Chris Wilson
@ 2020-07-10 11:26   ` Tvrtko Ursulin
  0 siblings, 0 replies; 65+ messages in thread
From: Tvrtko Ursulin @ 2020-07-10 11:26 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 06/07/2020 07:19, Chris Wilson wrote:
> Rename the current list of unbound objects so that we can track of all
> objects that we need to bind, as well as the list of currently unbound
> [unprocessed] objects.
> 
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> ---
>   drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c | 14 +++++++-------
>   1 file changed, 7 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> index e4d06db3f313..bf8193d9e279 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> @@ -33,7 +33,7 @@ struct eb_vma {
>   
>   	/** This vma's place in the execbuf reservation list */
>   	struct drm_i915_gem_exec_object2 *exec;
> -	struct list_head bind_link;
> +	struct list_head unbound_link;
>   	struct list_head reloc_link;
>   
>   	struct hlist_node node;
> @@ -594,7 +594,7 @@ eb_add_vma(struct i915_execbuffer *eb,
>   		}
>   	} else {
>   		eb_unreserve_vma(ev);
> -		list_add_tail(&ev->bind_link, &eb->unbound);
> +		list_add_tail(&ev->unbound_link, &eb->unbound);
>   	}
>   }
>   
> @@ -699,7 +699,7 @@ static int eb_reserve(struct i915_execbuffer *eb)
>   		if (mutex_lock_interruptible(&eb->i915->drm.struct_mutex))
>   			return -EINTR;
>   
> -		list_for_each_entry(ev, &eb->unbound, bind_link) {
> +		list_for_each_entry(ev, &eb->unbound, unbound_link) {
>   			err = eb_reserve_vma(eb, ev, pin_flags);
>   			if (err)
>   				break;
> @@ -725,15 +725,15 @@ static int eb_reserve(struct i915_execbuffer *eb)
>   
>   			if (flags & EXEC_OBJECT_PINNED)
>   				/* Pinned must have their slot */
> -				list_add(&ev->bind_link, &eb->unbound);
> +				list_add(&ev->unbound_link, &eb->unbound);
>   			else if (flags & __EXEC_OBJECT_NEEDS_MAP)
>   				/* Map require the lowest 256MiB (aperture) */
> -				list_add_tail(&ev->bind_link, &eb->unbound);
> +				list_add_tail(&ev->unbound_link, &eb->unbound);
>   			else if (!(flags & EXEC_OBJECT_SUPPORTS_48B_ADDRESS))
>   				/* Prioritise 4GiB region for restricted bo */
> -				list_add(&ev->bind_link, &last);
> +				list_add(&ev->unbound_link, &last);
>   			else
> -				list_add_tail(&ev->bind_link, &last);
> +				list_add_tail(&ev->unbound_link, &last);
>   		}
>   		list_splice_tail(&last, &eb->unbound);
>   		mutex_unlock(&eb->i915->drm.struct_mutex);
> 

Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>

Regards,

Tvrtko
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Intel-gfx] [PATCH 05/20] drm/i915/gem: Break apart the early i915_vma_pin from execbuf object lookup
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 05/20] drm/i915/gem: Break apart the early i915_vma_pin from execbuf object lookup Chris Wilson
@ 2020-07-10 11:27   ` Tvrtko Ursulin
  0 siblings, 0 replies; 65+ messages in thread
From: Tvrtko Ursulin @ 2020-07-10 11:27 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 06/07/2020 07:19, Chris Wilson wrote:
> As a prelude to the next step where we want to perform all the object
> allocations together under the same lock, we first must delay the
> i915_vma_pin() as that implicitly does the allocations for us, one by
> one. As it only does the allocations one by one, it is not allowed to
> wait/evict, whereas pulling all the allocations together the entire set
> can be scheduled as one.
> 
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> ---
>   .../gpu/drm/i915/gem/i915_gem_execbuffer.c    | 70 +++++++++++--------
>   1 file changed, 39 insertions(+), 31 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> index bf8193d9e279..35a57c1fc9c3 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> @@ -33,6 +33,8 @@ struct eb_vma {
>   
>   	/** This vma's place in the execbuf reservation list */
>   	struct drm_i915_gem_exec_object2 *exec;
> +
> +	struct list_head bind_link;
>   	struct list_head unbound_link;
>   	struct list_head reloc_link;
>   
> @@ -240,8 +242,8 @@ struct i915_execbuffer {
>   	/** actual size of execobj[] as we may extend it for the cmdparser */
>   	unsigned int buffer_count;
>   
> -	/** list of vma not yet bound during reservation phase */
> -	struct list_head unbound;
> +	/** list of all vma required to bound for this execbuf */
> +	struct list_head bind_list;
>   
>   	/** list of vma that have execobj.relocation_count */
>   	struct list_head relocs;
> @@ -565,6 +567,8 @@ eb_add_vma(struct i915_execbuffer *eb,
>   						    eb->lut_size)]);
>   	}
>   
> +	list_add_tail(&ev->bind_link, &eb->bind_list);
> +
>   	if (entry->relocation_count)
>   		list_add_tail(&ev->reloc_link, &eb->relocs);
>   
> @@ -586,16 +590,6 @@ eb_add_vma(struct i915_execbuffer *eb,
>   
>   		eb->batch = ev;
>   	}
> -
> -	if (eb_pin_vma(eb, entry, ev)) {
> -		if (entry->offset != vma->node.start) {
> -			entry->offset = vma->node.start | UPDATE;
> -			eb->args->flags |= __EXEC_HAS_RELOC;
> -		}
> -	} else {
> -		eb_unreserve_vma(ev);
> -		list_add_tail(&ev->unbound_link, &eb->unbound);
> -	}
>   }
>   
>   static int eb_reserve_vma(const struct i915_execbuffer *eb,
> @@ -670,13 +664,31 @@ static int wait_for_timeline(struct intel_timeline *tl)
>   	} while (1);
>   }
>   
> -static int eb_reserve(struct i915_execbuffer *eb)
> +static int eb_reserve_vm(struct i915_execbuffer *eb)
>   {
> -	const unsigned int count = eb->buffer_count;
>   	unsigned int pin_flags = PIN_USER | PIN_NONBLOCK;
> -	struct list_head last;
> +	struct list_head last, unbound;
>   	struct eb_vma *ev;
> -	unsigned int i, pass;
> +	unsigned int pass;
> +
> +	INIT_LIST_HEAD(&unbound);
> +	list_for_each_entry(ev, &eb->bind_list, bind_link) {
> +		struct drm_i915_gem_exec_object2 *entry = ev->exec;
> +		struct i915_vma *vma = ev->vma;
> +
> +		if (eb_pin_vma(eb, entry, ev)) {
> +			if (entry->offset != vma->node.start) {
> +				entry->offset = vma->node.start | UPDATE;
> +				eb->args->flags |= __EXEC_HAS_RELOC;
> +			}
> +		} else {
> +			eb_unreserve_vma(ev);
> +			list_add_tail(&ev->unbound_link, &unbound);
> +		}
> +	}
> +
> +	if (list_empty(&unbound))
> +		return 0;
>   
>   	/*
>   	 * Attempt to pin all of the buffers into the GTT.
> @@ -699,7 +711,7 @@ static int eb_reserve(struct i915_execbuffer *eb)
>   		if (mutex_lock_interruptible(&eb->i915->drm.struct_mutex))
>   			return -EINTR;
>   
> -		list_for_each_entry(ev, &eb->unbound, unbound_link) {
> +		list_for_each_entry(ev, &unbound, unbound_link) {
>   			err = eb_reserve_vma(eb, ev, pin_flags);
>   			if (err)
>   				break;
> @@ -710,13 +722,11 @@ static int eb_reserve(struct i915_execbuffer *eb)
>   		}
>   
>   		/* Resort *all* the objects into priority order */
> -		INIT_LIST_HEAD(&eb->unbound);
> +		INIT_LIST_HEAD(&unbound);
>   		INIT_LIST_HEAD(&last);
> -		for (i = 0; i < count; i++) {
> -			unsigned int flags;
> +		list_for_each_entry(ev, &eb->bind_list, bind_link) {
> +			unsigned int flags = ev->flags;
>   
> -			ev = &eb->vma[i];
> -			flags = ev->flags;
>   			if (flags & EXEC_OBJECT_PINNED &&
>   			    flags & __EXEC_OBJECT_HAS_PIN)
>   				continue;
> @@ -725,17 +735,17 @@ static int eb_reserve(struct i915_execbuffer *eb)
>   
>   			if (flags & EXEC_OBJECT_PINNED)
>   				/* Pinned must have their slot */
> -				list_add(&ev->unbound_link, &eb->unbound);
> +				list_add(&ev->unbound_link, &unbound);
>   			else if (flags & __EXEC_OBJECT_NEEDS_MAP)
>   				/* Map require the lowest 256MiB (aperture) */
> -				list_add_tail(&ev->unbound_link, &eb->unbound);
> +				list_add_tail(&ev->unbound_link, &unbound);
>   			else if (!(flags & EXEC_OBJECT_SUPPORTS_48B_ADDRESS))
>   				/* Prioritise 4GiB region for restricted bo */
>   				list_add(&ev->unbound_link, &last);
>   			else
>   				list_add_tail(&ev->unbound_link, &last);
>   		}
> -		list_splice_tail(&last, &eb->unbound);
> +		list_splice_tail(&last, &unbound);
>   		mutex_unlock(&eb->i915->drm.struct_mutex);
>   
>   		if (err == -EAGAIN) {
> @@ -891,8 +901,8 @@ static int eb_lookup_vmas(struct i915_execbuffer *eb)
>   	unsigned int i;
>   	int err = 0;
>   
> +	INIT_LIST_HEAD(&eb->bind_list);
>   	INIT_LIST_HEAD(&eb->relocs);
> -	INIT_LIST_HEAD(&eb->unbound);
>   
>   	for (i = 0; i < eb->buffer_count; i++) {
>   		struct i915_vma *vma;
> @@ -1539,11 +1549,9 @@ static int eb_relocate(struct i915_execbuffer *eb)
>   	if (err)
>   		return err;
>   
> -	if (!list_empty(&eb->unbound)) {
> -		err = eb_reserve(eb);
> -		if (err)
> -			return err;
> -	}
> +	err = eb_reserve_vm(eb);
> +	if (err)
> +		return err;
>   
>   	/* The objects are in their final locations, apply the relocations. */
>   	if (eb->args->flags & __EXEC_HAS_RELOC) {
> 

Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>

Regards,

Tvrtko
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Intel-gfx] [PATCH 10/20] drm/i915: Export a preallocate variant of i915_active_acquire()
  2020-07-09 14:36   ` Maarten Lankhorst
@ 2020-07-10 12:24     ` Tvrtko Ursulin
  2020-07-10 12:32       ` Maarten Lankhorst
  0 siblings, 1 reply; 65+ messages in thread
From: Tvrtko Ursulin @ 2020-07-10 12:24 UTC (permalink / raw)
  To: Maarten Lankhorst, Chris Wilson, intel-gfx


On 09/07/2020 15:36, Maarten Lankhorst wrote:
> Op 06-07-2020 om 08:19 schreef Chris Wilson:
>> Sometimes we have to be very careful not to allocate underneath a mutex
>> (or spinlock) and yet still want to track activity. Enter
>> i915_active_acquire_for_context(). This raises the activity counter on
>> i915_active prior to use and ensures that the fence-tree contains a slot
>> for the context.
>>
>> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
>> ---
>>   .../gpu/drm/i915/gem/i915_gem_execbuffer.c    |   2 +-
>>   drivers/gpu/drm/i915/gt/intel_timeline.c      |   4 +-
>>   drivers/gpu/drm/i915/i915_active.c            | 113 +++++++++++++++---
>>   drivers/gpu/drm/i915/i915_active.h            |  14 ++-
>>   4 files changed, 113 insertions(+), 20 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
>> index 1015c4fd9f3e..6d20be29ff3c 100644
>> --- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
>> +++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
>> @@ -1789,7 +1789,7 @@ __parser_mark_active(struct i915_vma *vma,
>>   {
>>   	struct intel_gt_buffer_pool_node *node = vma->private;
>>   
>> -	return i915_active_ref(&node->active, tl, fence);
>> +	return i915_active_ref(&node->active, tl->fence_context, fence);
>>   }
>>   
>>   static int
>> diff --git a/drivers/gpu/drm/i915/gt/intel_timeline.c b/drivers/gpu/drm/i915/gt/intel_timeline.c
>> index 4546284fede1..e4a5326633b8 100644
>> --- a/drivers/gpu/drm/i915/gt/intel_timeline.c
>> +++ b/drivers/gpu/drm/i915/gt/intel_timeline.c
>> @@ -479,7 +479,9 @@ __intel_timeline_get_seqno(struct intel_timeline *tl,
>>   	 * free it after the current request is retired, which ensures that
>>   	 * all writes into the cacheline from previous requests are complete.
>>   	 */
>> -	err = i915_active_ref(&tl->hwsp_cacheline->active, tl, &rq->fence);
>> +	err = i915_active_ref(&tl->hwsp_cacheline->active,
>> +			      tl->fence_context,
>> +			      &rq->fence);
>>   	if (err)
>>   		goto err_cacheline;
>>   
>> diff --git a/drivers/gpu/drm/i915/i915_active.c b/drivers/gpu/drm/i915/i915_active.c
>> index d960d0be5bd2..3f595446fd44 100644
>> --- a/drivers/gpu/drm/i915/i915_active.c
>> +++ b/drivers/gpu/drm/i915/i915_active.c
>> @@ -217,11 +217,10 @@ excl_retire(struct dma_fence *fence, struct dma_fence_cb *cb)
>>   }
>>   
>>   static struct i915_active_fence *
>> -active_instance(struct i915_active *ref, struct intel_timeline *tl)
>> +active_instance(struct i915_active *ref, u64 idx)
>>   {
>>   	struct active_node *node, *prealloc;
>>   	struct rb_node **p, *parent;
>> -	u64 idx = tl->fence_context;
>>   
>>   	/*
>>   	 * We track the most recently used timeline to skip a rbtree search
>> @@ -353,21 +352,17 @@ __active_del_barrier(struct i915_active *ref, struct active_node *node)
>>   	return ____active_del_barrier(ref, node, barrier_to_engine(node));
>>   }
>>   
>> -int i915_active_ref(struct i915_active *ref,
>> -		    struct intel_timeline *tl,
>> -		    struct dma_fence *fence)
>> +int i915_active_ref(struct i915_active *ref, u64 idx, struct dma_fence *fence)
>>   {
>>   	struct i915_active_fence *active;
>>   	int err;
>>   
>> -	lockdep_assert_held(&tl->mutex);
>> -
>>   	/* Prevent reaping in case we malloc/wait while building the tree */
>>   	err = i915_active_acquire(ref);
>>   	if (err)
>>   		return err;
>>   
>> -	active = active_instance(ref, tl);
>> +	active = active_instance(ref, idx);
>>   	if (!active) {
>>   		err = -ENOMEM;
>>   		goto out;
>> @@ -384,32 +379,104 @@ int i915_active_ref(struct i915_active *ref,
>>   		atomic_dec(&ref->count);
>>   	}
>>   	if (!__i915_active_fence_set(active, fence))
>> -		atomic_inc(&ref->count);
>> +		__i915_active_acquire(ref);
>>   
>>   out:
>>   	i915_active_release(ref);
>>   	return err;
>>   }
>>   
>> -struct dma_fence *
>> -i915_active_set_exclusive(struct i915_active *ref, struct dma_fence *f)
>> +static struct dma_fence *
>> +__i915_active_set_fence(struct i915_active *ref,
>> +			struct i915_active_fence *active,
>> +			struct dma_fence *fence)
>>   {
>>   	struct dma_fence *prev;
>>   
>>   	/* We expect the caller to manage the exclusive timeline ordering */
>>   	GEM_BUG_ON(i915_active_is_idle(ref));
>>   
>> +	if (is_barrier(active)) { /* proto-node used by our idle barrier */
>> +		/*
>> +		 * This request is on the kernel_context timeline, and so
>> +		 * we can use it to substitute for the pending idle-barrer
>> +		 * request that we want to emit on the kernel_context.
>> +		 */
>> +		__active_del_barrier(ref, node_from_active(active));
>> +		RCU_INIT_POINTER(active->fence, NULL);
>> +		atomic_dec(&ref->count);
>> +	}
>> +
>>   	rcu_read_lock();
>> -	prev = __i915_active_fence_set(&ref->excl, f);
>> +	prev = __i915_active_fence_set(active, fence);
>>   	if (prev)
>>   		prev = dma_fence_get_rcu(prev);
>>   	else
>> -		atomic_inc(&ref->count);
>> +		__i915_active_acquire(ref);
>>   	rcu_read_unlock();
>>   
>>   	return prev;
>>   }
>>   
>> +static struct i915_active_fence *
>> +__active_lookup(struct i915_active *ref, u64 idx)
>> +{
>> +	struct active_node *node;
>> +	struct rb_node *p;
>> +
>> +	/* Like active_instance() but with no malloc */
>> +
>> +	node = READ_ONCE(ref->cache);
>> +	if (node && node->timeline == idx)
>> +		return &node->base;
>> +
>> +	spin_lock_irq(&ref->tree_lock);
>> +	GEM_BUG_ON(i915_active_is_idle(ref));
>> +
>> +	p = ref->tree.rb_node;
>> +	while (p) {
>> +		node = rb_entry(p, struct active_node, node);
>> +		if (node->timeline == idx) {
>> +			ref->cache = node;
>> +			spin_unlock_irq(&ref->tree_lock);
>> +			return &node->base;
>> +		}
>> +
>> +		if (node->timeline < idx)
>> +			p = p->rb_right;
>> +		else
>> +			p = p->rb_left;
>> +	}
>> +
>> +	spin_unlock_irq(&ref->tree_lock);
>> +
>> +	return NULL;
>> +}
>> +
>> +struct dma_fence *
>> +__i915_active_ref(struct i915_active *ref, u64 idx, struct dma_fence *fence)
>> +{
>> +	struct dma_fence *prev = ERR_PTR(-ENOENT);
>> +	struct i915_active_fence *active;
>> +
>> +	if (!i915_active_acquire_if_busy(ref))
>> +		return ERR_PTR(-EINVAL);
>> +
>> +	active = __active_lookup(ref, idx);
>> +	if (active)
>> +		prev = __i915_active_set_fence(ref, active, fence);
>> +
>> +	i915_active_release(ref);
>> +	return prev;
>> +}
>> +
>> +struct dma_fence *
>> +i915_active_set_exclusive(struct i915_active *ref, struct dma_fence *f)
>> +{
>> +	/* We expect the caller to manage the exclusive timeline ordering */
>> +	return __i915_active_set_fence(ref, &ref->excl, f);
>> +}
>> +
>>   bool i915_active_acquire_if_busy(struct i915_active *ref)
>>   {
>>   	debug_active_assert(ref);
>> @@ -443,6 +510,24 @@ int i915_active_acquire(struct i915_active *ref)
>>   	return err;
>>   }
>>   
>> +int i915_active_acquire_for_context(struct i915_active *ref, u64 idx)
>> +{
>> +	struct i915_active_fence *active;
>> +	int err;
>> +
>> +	err = i915_active_acquire(ref);
>> +	if (err)
>> +		return err;
>> +
>> +	active = active_instance(ref, idx);
>> +	if (!active) {
>> +		i915_active_release(ref);
>> +		return -ENOMEM;
>> +	}
>> +
>> +	return 0; /* return with active ref */
>> +}
>> +
>>   void i915_active_release(struct i915_active *ref)
>>   {
>>   	debug_active_assert(ref);
>> @@ -804,7 +889,7 @@ int i915_active_acquire_preallocate_barrier(struct i915_active *ref,
>>   			 */
>>   			RCU_INIT_POINTER(node->base.fence, ERR_PTR(-EAGAIN));
>>   			node->base.cb.node.prev = (void *)engine;
>> -			atomic_inc(&ref->count);
>> +			__i915_active_acquire(ref);
>>   		}
>>   		GEM_BUG_ON(rcu_access_pointer(node->base.fence) != ERR_PTR(-EAGAIN));
>>   
>> diff --git a/drivers/gpu/drm/i915/i915_active.h b/drivers/gpu/drm/i915/i915_active.h
>> index cf4058150966..2e0bcb3289ec 100644
>> --- a/drivers/gpu/drm/i915/i915_active.h
>> +++ b/drivers/gpu/drm/i915/i915_active.h
>> @@ -163,14 +163,18 @@ void __i915_active_init(struct i915_active *ref,
>>   	__i915_active_init(ref, active, retire, &__mkey, &__wkey);	\
>>   } while (0)
>>   
>> -int i915_active_ref(struct i915_active *ref,
>> -		    struct intel_timeline *tl,
>> -		    struct dma_fence *fence);
>> +struct dma_fence *
>> +__i915_active_ref(struct i915_active *ref, u64 idx, struct dma_fence *fence);
>> +int i915_active_ref(struct i915_active *ref, u64 idx, struct dma_fence *fence);
>>   
>>   static inline int
>>   i915_active_add_request(struct i915_active *ref, struct i915_request *rq)
>>   {
>> -	return i915_active_ref(ref, i915_request_timeline(rq), &rq->fence);
>> +	struct intel_timeline *tl = i915_request_timeline(rq);
>> +
>> +	lockdep_assert_held(&tl->mutex);
>> +
>> +	return i915_active_ref(ref, tl->fence_context, &rq->fence);
>>   }
>>   
>>   struct dma_fence *
>> @@ -198,7 +202,9 @@ int i915_request_await_active(struct i915_request *rq,
>>   #define I915_ACTIVE_AWAIT_BARRIER BIT(2)
>>   
>>   int i915_active_acquire(struct i915_active *ref);
>> +int i915_active_acquire_for_context(struct i915_active *ref, u64 idx);
>>   bool i915_active_acquire_if_busy(struct i915_active *ref);
>> +
>>   void i915_active_release(struct i915_active *ref);
>>   
>>   static inline void __i915_active_acquire(struct i915_active *ref)
> 
> Somewhere this is hiding a locking inversion in the later part of this series.
> 
> tl->mutex needs to be an inner lock of ww objects, see my patch series.
> 
> Since this is not the case, you're somehow hiding a locking inversion.

Which locks you think and from where? I want to make sure I am not 
missing something while reading the series..

There used to be (still is in upstream) a possible deadlock with async 
get pages if batch buffer was an userptr object, but this series solves 
that by pre-allocating page directories in the reservation phase. That's 
not a plain locking inversion, but maybe that's what you had in mind?

Does it take tl->mutex undeer dma_fence_wait? I can't see that it does.

Can't think of any other options at the moment.

Regards,

Tvrtko
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Intel-gfx] [PATCH 10/20] drm/i915: Export a preallocate variant of i915_active_acquire()
  2020-07-10 12:24     ` Tvrtko Ursulin
@ 2020-07-10 12:32       ` Maarten Lankhorst
  0 siblings, 0 replies; 65+ messages in thread
From: Maarten Lankhorst @ 2020-07-10 12:32 UTC (permalink / raw)
  To: Tvrtko Ursulin, Chris Wilson, intel-gfx

Op 10-07-2020 om 14:24 schreef Tvrtko Ursulin:
>
> On 09/07/2020 15:36, Maarten Lankhorst wrote:
>> Op 06-07-2020 om 08:19 schreef Chris Wilson:
>>> Sometimes we have to be very careful not to allocate underneath a mutex
>>> (or spinlock) and yet still want to track activity. Enter
>>> i915_active_acquire_for_context(). This raises the activity counter on
>>> i915_active prior to use and ensures that the fence-tree contains a slot
>>> for the context.
>>>
>>> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
>>> ---
>>>   .../gpu/drm/i915/gem/i915_gem_execbuffer.c    |   2 +-
>>>   drivers/gpu/drm/i915/gt/intel_timeline.c      |   4 +-
>>>   drivers/gpu/drm/i915/i915_active.c            | 113 +++++++++++++++---
>>>   drivers/gpu/drm/i915/i915_active.h            |  14 ++-
>>>   4 files changed, 113 insertions(+), 20 deletions(-)
>>>
>>> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
>>> index 1015c4fd9f3e..6d20be29ff3c 100644
>>> --- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
>>> +++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
>>> @@ -1789,7 +1789,7 @@ __parser_mark_active(struct i915_vma *vma,
>>>   {
>>>       struct intel_gt_buffer_pool_node *node = vma->private;
>>>   -    return i915_active_ref(&node->active, tl, fence);
>>> +    return i915_active_ref(&node->active, tl->fence_context, fence);
>>>   }
>>>     static int
>>> diff --git a/drivers/gpu/drm/i915/gt/intel_timeline.c b/drivers/gpu/drm/i915/gt/intel_timeline.c
>>> index 4546284fede1..e4a5326633b8 100644
>>> --- a/drivers/gpu/drm/i915/gt/intel_timeline.c
>>> +++ b/drivers/gpu/drm/i915/gt/intel_timeline.c
>>> @@ -479,7 +479,9 @@ __intel_timeline_get_seqno(struct intel_timeline *tl,
>>>        * free it after the current request is retired, which ensures that
>>>        * all writes into the cacheline from previous requests are complete.
>>>        */
>>> -    err = i915_active_ref(&tl->hwsp_cacheline->active, tl, &rq->fence);
>>> +    err = i915_active_ref(&tl->hwsp_cacheline->active,
>>> +                  tl->fence_context,
>>> +                  &rq->fence);
>>>       if (err)
>>>           goto err_cacheline;
>>>   diff --git a/drivers/gpu/drm/i915/i915_active.c b/drivers/gpu/drm/i915/i915_active.c
>>> index d960d0be5bd2..3f595446fd44 100644
>>> --- a/drivers/gpu/drm/i915/i915_active.c
>>> +++ b/drivers/gpu/drm/i915/i915_active.c
>>> @@ -217,11 +217,10 @@ excl_retire(struct dma_fence *fence, struct dma_fence_cb *cb)
>>>   }
>>>     static struct i915_active_fence *
>>> -active_instance(struct i915_active *ref, struct intel_timeline *tl)
>>> +active_instance(struct i915_active *ref, u64 idx)
>>>   {
>>>       struct active_node *node, *prealloc;
>>>       struct rb_node **p, *parent;
>>> -    u64 idx = tl->fence_context;
>>>         /*
>>>        * We track the most recently used timeline to skip a rbtree search
>>> @@ -353,21 +352,17 @@ __active_del_barrier(struct i915_active *ref, struct active_node *node)
>>>       return ____active_del_barrier(ref, node, barrier_to_engine(node));
>>>   }
>>>   -int i915_active_ref(struct i915_active *ref,
>>> -            struct intel_timeline *tl,
>>> -            struct dma_fence *fence)
>>> +int i915_active_ref(struct i915_active *ref, u64 idx, struct dma_fence *fence)
>>>   {
>>>       struct i915_active_fence *active;
>>>       int err;
>>>   -    lockdep_assert_held(&tl->mutex);
>>> -
>>>       /* Prevent reaping in case we malloc/wait while building the tree */
>>>       err = i915_active_acquire(ref);
>>>       if (err)
>>>           return err;
>>>   -    active = active_instance(ref, tl);
>>> +    active = active_instance(ref, idx);
>>>       if (!active) {
>>>           err = -ENOMEM;
>>>           goto out;
>>> @@ -384,32 +379,104 @@ int i915_active_ref(struct i915_active *ref,
>>>           atomic_dec(&ref->count);
>>>       }
>>>       if (!__i915_active_fence_set(active, fence))
>>> -        atomic_inc(&ref->count);
>>> +        __i915_active_acquire(ref);
>>>     out:
>>>       i915_active_release(ref);
>>>       return err;
>>>   }
>>>   -struct dma_fence *
>>> -i915_active_set_exclusive(struct i915_active *ref, struct dma_fence *f)
>>> +static struct dma_fence *
>>> +__i915_active_set_fence(struct i915_active *ref,
>>> +            struct i915_active_fence *active,
>>> +            struct dma_fence *fence)
>>>   {
>>>       struct dma_fence *prev;
>>>         /* We expect the caller to manage the exclusive timeline ordering */
>>>       GEM_BUG_ON(i915_active_is_idle(ref));
>>>   +    if (is_barrier(active)) { /* proto-node used by our idle barrier */
>>> +        /*
>>> +         * This request is on the kernel_context timeline, and so
>>> +         * we can use it to substitute for the pending idle-barrer
>>> +         * request that we want to emit on the kernel_context.
>>> +         */
>>> +        __active_del_barrier(ref, node_from_active(active));
>>> +        RCU_INIT_POINTER(active->fence, NULL);
>>> +        atomic_dec(&ref->count);
>>> +    }
>>> +
>>>       rcu_read_lock();
>>> -    prev = __i915_active_fence_set(&ref->excl, f);
>>> +    prev = __i915_active_fence_set(active, fence);
>>>       if (prev)
>>>           prev = dma_fence_get_rcu(prev);
>>>       else
>>> -        atomic_inc(&ref->count);
>>> +        __i915_active_acquire(ref);
>>>       rcu_read_unlock();
>>>         return prev;
>>>   }
>>>   +static struct i915_active_fence *
>>> +__active_lookup(struct i915_active *ref, u64 idx)
>>> +{
>>> +    struct active_node *node;
>>> +    struct rb_node *p;
>>> +
>>> +    /* Like active_instance() but with no malloc */
>>> +
>>> +    node = READ_ONCE(ref->cache);
>>> +    if (node && node->timeline == idx)
>>> +        return &node->base;
>>> +
>>> +    spin_lock_irq(&ref->tree_lock);
>>> +    GEM_BUG_ON(i915_active_is_idle(ref));
>>> +
>>> +    p = ref->tree.rb_node;
>>> +    while (p) {
>>> +        node = rb_entry(p, struct active_node, node);
>>> +        if (node->timeline == idx) {
>>> +            ref->cache = node;
>>> +            spin_unlock_irq(&ref->tree_lock);
>>> +            return &node->base;
>>> +        }
>>> +
>>> +        if (node->timeline < idx)
>>> +            p = p->rb_right;
>>> +        else
>>> +            p = p->rb_left;
>>> +    }
>>> +
>>> +    spin_unlock_irq(&ref->tree_lock);
>>> +
>>> +    return NULL;
>>> +}
>>> +
>>> +struct dma_fence *
>>> +__i915_active_ref(struct i915_active *ref, u64 idx, struct dma_fence *fence)
>>> +{
>>> +    struct dma_fence *prev = ERR_PTR(-ENOENT);
>>> +    struct i915_active_fence *active;
>>> +
>>> +    if (!i915_active_acquire_if_busy(ref))
>>> +        return ERR_PTR(-EINVAL);
>>> +
>>> +    active = __active_lookup(ref, idx);
>>> +    if (active)
>>> +        prev = __i915_active_set_fence(ref, active, fence);
>>> +
>>> +    i915_active_release(ref);
>>> +    return prev;
>>> +}
>>> +
>>> +struct dma_fence *
>>> +i915_active_set_exclusive(struct i915_active *ref, struct dma_fence *f)
>>> +{
>>> +    /* We expect the caller to manage the exclusive timeline ordering */
>>> +    return __i915_active_set_fence(ref, &ref->excl, f);
>>> +}
>>> +
>>>   bool i915_active_acquire_if_busy(struct i915_active *ref)
>>>   {
>>>       debug_active_assert(ref);
>>> @@ -443,6 +510,24 @@ int i915_active_acquire(struct i915_active *ref)
>>>       return err;
>>>   }
>>>   +int i915_active_acquire_for_context(struct i915_active *ref, u64 idx)
>>> +{
>>> +    struct i915_active_fence *active;
>>> +    int err;
>>> +
>>> +    err = i915_active_acquire(ref);
>>> +    if (err)
>>> +        return err;
>>> +
>>> +    active = active_instance(ref, idx);
>>> +    if (!active) {
>>> +        i915_active_release(ref);
>>> +        return -ENOMEM;
>>> +    }
>>> +
>>> +    return 0; /* return with active ref */
>>> +}
>>> +
>>>   void i915_active_release(struct i915_active *ref)
>>>   {
>>>       debug_active_assert(ref);
>>> @@ -804,7 +889,7 @@ int i915_active_acquire_preallocate_barrier(struct i915_active *ref,
>>>                */
>>>               RCU_INIT_POINTER(node->base.fence, ERR_PTR(-EAGAIN));
>>>               node->base.cb.node.prev = (void *)engine;
>>> -            atomic_inc(&ref->count);
>>> +            __i915_active_acquire(ref);
>>>           }
>>>           GEM_BUG_ON(rcu_access_pointer(node->base.fence) != ERR_PTR(-EAGAIN));
>>>   diff --git a/drivers/gpu/drm/i915/i915_active.h b/drivers/gpu/drm/i915/i915_active.h
>>> index cf4058150966..2e0bcb3289ec 100644
>>> --- a/drivers/gpu/drm/i915/i915_active.h
>>> +++ b/drivers/gpu/drm/i915/i915_active.h
>>> @@ -163,14 +163,18 @@ void __i915_active_init(struct i915_active *ref,
>>>       __i915_active_init(ref, active, retire, &__mkey, &__wkey);    \
>>>   } while (0)
>>>   -int i915_active_ref(struct i915_active *ref,
>>> -            struct intel_timeline *tl,
>>> -            struct dma_fence *fence);
>>> +struct dma_fence *
>>> +__i915_active_ref(struct i915_active *ref, u64 idx, struct dma_fence *fence);
>>> +int i915_active_ref(struct i915_active *ref, u64 idx, struct dma_fence *fence);
>>>     static inline int
>>>   i915_active_add_request(struct i915_active *ref, struct i915_request *rq)
>>>   {
>>> -    return i915_active_ref(ref, i915_request_timeline(rq), &rq->fence);
>>> +    struct intel_timeline *tl = i915_request_timeline(rq);
>>> +
>>> +    lockdep_assert_held(&tl->mutex);
>>> +
>>> +    return i915_active_ref(ref, tl->fence_context, &rq->fence);
>>>   }
>>>     struct dma_fence *
>>> @@ -198,7 +202,9 @@ int i915_request_await_active(struct i915_request *rq,
>>>   #define I915_ACTIVE_AWAIT_BARRIER BIT(2)
>>>     int i915_active_acquire(struct i915_active *ref);
>>> +int i915_active_acquire_for_context(struct i915_active *ref, u64 idx);
>>>   bool i915_active_acquire_if_busy(struct i915_active *ref);
>>> +
>>>   void i915_active_release(struct i915_active *ref);
>>>     static inline void __i915_active_acquire(struct i915_active *ref)
>>
>> Somewhere this is hiding a locking inversion in the later part of this series.
>>
>> tl->mutex needs to be an inner lock of ww objects, see my patch series.
>>
>> Since this is not the case, you're somehow hiding a locking inversion.
>
> Which locks you think and from where? I want to make sure I am not missing something while reading the series..
>
> There used to be (still is in upstream) a possible deadlock with async get pages if batch buffer was an userptr object, but this series solves that by pre-allocating page directories in the reservation phase. That's not a plain locking inversion, but maybe that's what you had in mind?
>
> Does it take tl->mutex undeer dma_fence_wait? I can't see that it does.
>
> Can't think of any other options at the moment.
>
> Regards,
>
> Tvrtko

Ah never mind, context pinning/throttling is not reworked yet here.

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Intel-gfx] [PATCH 09/20] drm/i915/gem: Assign context id for async work
  2020-07-09 12:07                 ` Chris Wilson
@ 2020-07-13 12:22                   ` Tvrtko Ursulin
  2020-07-14 14:01                     ` Chris Wilson
  0 siblings, 1 reply; 65+ messages in thread
From: Tvrtko Ursulin @ 2020-07-13 12:22 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 09/07/2020 13:07, Chris Wilson wrote:
> Quoting Tvrtko Ursulin (2020-07-09 12:59:51)
>>
>> On 09/07/2020 12:07, Chris Wilson wrote:
>>> Quoting Tvrtko Ursulin (2020-07-09 12:01:29)
>>>>
>>>> On 08/07/2020 16:36, Chris Wilson wrote:
>>>>> Quoting Tvrtko Ursulin (2020-07-08 15:24:20)
>>>>>> And what is the effective behaviour you get with N contexts - emit N
>>>>>> concurrent operations and for N + 1 block in execbuf?
>>>>>
>>>>> Each context defines a timeline. A task is not ready to run until the
>>>>> task before it in its timeline is completed. So we don't block in
>>>>> execbuf, the scheduler waits until the request is ready before putting
>>>>> it into the HW queues -- i.e. the number chain of fences with everything
>>>>> that entails about ensuring it runs to completion [whether successfully
>>>>> or not, if not we then rely on the error propagation to limit the damage
>>>>> and report it back to the user if they kept a fence around to inspect].
>>>>
>>>> Okay but what is the benefit of N contexts in this series, before the
>>>> work is actually spread over ctx async width CPUs? Is there any? If not
>>>> I would prefer this patch is delayed until the time some actual
>>>> parallelism is ready to be added.
>>>
>>> We currently submit an unbounded amount of work. This patch is added
>>> along with its user to restrict the amount of work allowed to run in
>>> parallel, and also is used to [crudely] serialise the multiple threads
>>> attempting to allocate space in the vm when we completely exhaust that
>>> address space. We need at least one fence-context id for each user, this
>>> took the opportunity to generalise that to N ids for each user.
>>
>> Right, this is what I asked at the beginning - restricting amount of
>> work run in parallel - does mean there is some "blocking"/serialisation
>> during execbuf? Or it is all async but then what is restricted?
> 
> It's all* async, so the number of workqueues we utilise is restricted,
> and so limits the number of CPUs we allow the one context to spread
> across with multiple execbufs.
> 
> *fsvo all.

Okay.

Related topic - have we ever thought about what happens when fence 
context id wraps? I know it's 64-bit, and even with this patch giving 
out num_cpus blocks, it still feels impossible that it would wrap in 
normal use. But I wonder if malicious client could create/destroy 
contexts to cause a wrap and then how well we handle it. I am probably 
just underestimating today how big 64-bit is and how many ioctls that 
would require..

Regards,

Tvrtko
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Intel-gfx] [PATCH 10/20] drm/i915: Export a preallocate variant of i915_active_acquire()
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 10/20] drm/i915: Export a preallocate variant of i915_active_acquire() Chris Wilson
  2020-07-09 14:36   ` Maarten Lankhorst
@ 2020-07-13 14:29   ` Tvrtko Ursulin
  1 sibling, 0 replies; 65+ messages in thread
From: Tvrtko Ursulin @ 2020-07-13 14:29 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 06/07/2020 07:19, Chris Wilson wrote:
> Sometimes we have to be very careful not to allocate underneath a mutex
> (or spinlock) and yet still want to track activity. Enter
> i915_active_acquire_for_context(). This raises the activity counter on
> i915_active prior to use and ensures that the fence-tree contains a slot
> for the context.
> 
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> ---
>   .../gpu/drm/i915/gem/i915_gem_execbuffer.c    |   2 +-
>   drivers/gpu/drm/i915/gt/intel_timeline.c      |   4 +-
>   drivers/gpu/drm/i915/i915_active.c            | 113 +++++++++++++++---
>   drivers/gpu/drm/i915/i915_active.h            |  14 ++-
>   4 files changed, 113 insertions(+), 20 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> index 1015c4fd9f3e..6d20be29ff3c 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> @@ -1789,7 +1789,7 @@ __parser_mark_active(struct i915_vma *vma,
>   {
>   	struct intel_gt_buffer_pool_node *node = vma->private;
>   
> -	return i915_active_ref(&node->active, tl, fence);
> +	return i915_active_ref(&node->active, tl->fence_context, fence);
>   }
>   
>   static int
> diff --git a/drivers/gpu/drm/i915/gt/intel_timeline.c b/drivers/gpu/drm/i915/gt/intel_timeline.c
> index 4546284fede1..e4a5326633b8 100644
> --- a/drivers/gpu/drm/i915/gt/intel_timeline.c
> +++ b/drivers/gpu/drm/i915/gt/intel_timeline.c
> @@ -479,7 +479,9 @@ __intel_timeline_get_seqno(struct intel_timeline *tl,
>   	 * free it after the current request is retired, which ensures that
>   	 * all writes into the cacheline from previous requests are complete.
>   	 */
> -	err = i915_active_ref(&tl->hwsp_cacheline->active, tl, &rq->fence);
> +	err = i915_active_ref(&tl->hwsp_cacheline->active,
> +			      tl->fence_context,
> +			      &rq->fence);
>   	if (err)
>   		goto err_cacheline;
>   
> diff --git a/drivers/gpu/drm/i915/i915_active.c b/drivers/gpu/drm/i915/i915_active.c
> index d960d0be5bd2..3f595446fd44 100644
> --- a/drivers/gpu/drm/i915/i915_active.c
> +++ b/drivers/gpu/drm/i915/i915_active.c
> @@ -217,11 +217,10 @@ excl_retire(struct dma_fence *fence, struct dma_fence_cb *cb)
>   }
>   
>   static struct i915_active_fence *
> -active_instance(struct i915_active *ref, struct intel_timeline *tl)
> +active_instance(struct i915_active *ref, u64 idx)
>   {
>   	struct active_node *node, *prealloc;
>   	struct rb_node **p, *parent;
> -	u64 idx = tl->fence_context;
>   
>   	/*
>   	 * We track the most recently used timeline to skip a rbtree search
> @@ -353,21 +352,17 @@ __active_del_barrier(struct i915_active *ref, struct active_node *node)
>   	return ____active_del_barrier(ref, node, barrier_to_engine(node));
>   }
>   
> -int i915_active_ref(struct i915_active *ref,
> -		    struct intel_timeline *tl,
> -		    struct dma_fence *fence)
> +int i915_active_ref(struct i915_active *ref, u64 idx, struct dma_fence *fence)
>   {
>   	struct i915_active_fence *active;
>   	int err;
>   
> -	lockdep_assert_held(&tl->mutex);
> -
>   	/* Prevent reaping in case we malloc/wait while building the tree */
>   	err = i915_active_acquire(ref);
>   	if (err)
>   		return err;
>   
> -	active = active_instance(ref, tl);
> +	active = active_instance(ref, idx);
>   	if (!active) {
>   		err = -ENOMEM;
>   		goto out;
> @@ -384,32 +379,104 @@ int i915_active_ref(struct i915_active *ref,
>   		atomic_dec(&ref->count);
>   	}
>   	if (!__i915_active_fence_set(active, fence))
> -		atomic_inc(&ref->count);
> +		__i915_active_acquire(ref);
>   
>   out:
>   	i915_active_release(ref);
>   	return err;
>   }
>   
> -struct dma_fence *
> -i915_active_set_exclusive(struct i915_active *ref, struct dma_fence *f)
> +static struct dma_fence *
> +__i915_active_set_fence(struct i915_active *ref,
> +			struct i915_active_fence *active,
> +			struct dma_fence *fence)
>   {
>   	struct dma_fence *prev;
>   
>   	/* We expect the caller to manage the exclusive timeline ordering */
>   	GEM_BUG_ON(i915_active_is_idle(ref));
>   
> +	if (is_barrier(active)) { /* proto-node used by our idle barrier */
> +		/*
> +		 * This request is on the kernel_context timeline, and so
> +		 * we can use it to substitute for the pending idle-barrer
> +		 * request that we want to emit on the kernel_context.
> +		 */
> +		__active_del_barrier(ref, node_from_active(active));
> +		RCU_INIT_POINTER(active->fence, NULL);

The condition, comments and operation up to here is duplicated from 
i915_active_ref not shown in the diff.

More important question is on the relationship between i915_active_ref 
and __i915_active_ref. Latter actually calls this new 
__i915_active_set_fence which is kind of surprising considering the 
usual patterns.

Is there any duplication of the APIs ie. opportunity to consolidate? For 
instance should i915_active_ref be broken up into 
preallocate/lookup/install helpers too?

Another confusing end result is that we'd end up with both 
__i915_active_fence_set(active-fence, dma-fence) and
__i915_active_set_fence(active, active-fence, dma-fence).

I am not offering any solutions just raising oddities at this point. :)

Regards,

Tvrtko

> +		atomic_dec(&ref->count);
> +	}
> +
>   	rcu_read_lock();
> -	prev = __i915_active_fence_set(&ref->excl, f);
> +	prev = __i915_active_fence_set(active, fence);
>   	if (prev)
>   		prev = dma_fence_get_rcu(prev);
>   	else
> -		atomic_inc(&ref->count);
> +		__i915_active_acquire(ref);
>   	rcu_read_unlock();
>   
>   	return prev;
>   }
>   
> +static struct i915_active_fence *
> +__active_lookup(struct i915_active *ref, u64 idx)
> +{
> +	struct active_node *node;
> +	struct rb_node *p;
> +
> +	/* Like active_instance() but with no malloc */
> +
> +	node = READ_ONCE(ref->cache);
> +	if (node && node->timeline == idx)
> +		return &node->base;
> +
> +	spin_lock_irq(&ref->tree_lock);
> +	GEM_BUG_ON(i915_active_is_idle(ref));
> +
> +	p = ref->tree.rb_node;
> +	while (p) {
> +		node = rb_entry(p, struct active_node, node);
> +		if (node->timeline == idx) {
> +			ref->cache = node;
> +			spin_unlock_irq(&ref->tree_lock);
> +			return &node->base;
> +		}
> +
> +		if (node->timeline < idx)
> +			p = p->rb_right;
> +		else
> +			p = p->rb_left;
> +	}
> +
> +	spin_unlock_irq(&ref->tree_lock);
> +
> +	return NULL;
> +}
> +
> +struct dma_fence *
> +__i915_active_ref(struct i915_active *ref, u64 idx, struct dma_fence *fence)
> +{
> +	struct dma_fence *prev = ERR_PTR(-ENOENT);
> +	struct i915_active_fence *active;
> +
> +	if (!i915_active_acquire_if_busy(ref))
> +		return ERR_PTR(-EINVAL);
> +
> +	active = __active_lookup(ref, idx);
> +	if (active)
> +		prev = __i915_active_set_fence(ref, active, fence);
> +
> +	i915_active_release(ref);
> +	return prev;
> +}
> +
> +struct dma_fence *
> +i915_active_set_exclusive(struct i915_active *ref, struct dma_fence *f)
> +{
> +	/* We expect the caller to manage the exclusive timeline ordering */
> +	return __i915_active_set_fence(ref, &ref->excl, f);
> +}
> +
>   bool i915_active_acquire_if_busy(struct i915_active *ref)
>   {
>   	debug_active_assert(ref);
> @@ -443,6 +510,24 @@ int i915_active_acquire(struct i915_active *ref)
>   	return err;
>   }
>   
> +int i915_active_acquire_for_context(struct i915_active *ref, u64 idx)
> +{
> +	struct i915_active_fence *active;
> +	int err;
> +
> +	err = i915_active_acquire(ref);
> +	if (err)
> +		return err;
> +
> +	active = active_instance(ref, idx);
> +	if (!active) {
> +		i915_active_release(ref);
> +		return -ENOMEM;
> +	}
> +
> +	return 0; /* return with active ref */
> +}
> +
>   void i915_active_release(struct i915_active *ref)
>   {
>   	debug_active_assert(ref);
> @@ -804,7 +889,7 @@ int i915_active_acquire_preallocate_barrier(struct i915_active *ref,
>   			 */
>   			RCU_INIT_POINTER(node->base.fence, ERR_PTR(-EAGAIN));
>   			node->base.cb.node.prev = (void *)engine;
> -			atomic_inc(&ref->count);
> +			__i915_active_acquire(ref);
>   		}
>   		GEM_BUG_ON(rcu_access_pointer(node->base.fence) != ERR_PTR(-EAGAIN));
>   
> diff --git a/drivers/gpu/drm/i915/i915_active.h b/drivers/gpu/drm/i915/i915_active.h
> index cf4058150966..2e0bcb3289ec 100644
> --- a/drivers/gpu/drm/i915/i915_active.h
> +++ b/drivers/gpu/drm/i915/i915_active.h
> @@ -163,14 +163,18 @@ void __i915_active_init(struct i915_active *ref,
>   	__i915_active_init(ref, active, retire, &__mkey, &__wkey);	\
>   } while (0)
>   
> -int i915_active_ref(struct i915_active *ref,
> -		    struct intel_timeline *tl,
> -		    struct dma_fence *fence);
> +struct dma_fence *
> +__i915_active_ref(struct i915_active *ref, u64 idx, struct dma_fence *fence);
> +int i915_active_ref(struct i915_active *ref, u64 idx, struct dma_fence *fence);
>   
>   static inline int
>   i915_active_add_request(struct i915_active *ref, struct i915_request *rq)
>   {
> -	return i915_active_ref(ref, i915_request_timeline(rq), &rq->fence);
> +	struct intel_timeline *tl = i915_request_timeline(rq);
> +
> +	lockdep_assert_held(&tl->mutex);
> +
> +	return i915_active_ref(ref, tl->fence_context, &rq->fence);
>   }
>   
>   struct dma_fence *
> @@ -198,7 +202,9 @@ int i915_request_await_active(struct i915_request *rq,
>   #define I915_ACTIVE_AWAIT_BARRIER BIT(2)
>   
>   int i915_active_acquire(struct i915_active *ref);
> +int i915_active_acquire_for_context(struct i915_active *ref, u64 idx);
>   bool i915_active_acquire_if_busy(struct i915_active *ref);
> +
>   void i915_active_release(struct i915_active *ref);
>   
>   static inline void __i915_active_acquire(struct i915_active *ref)
> 
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Intel-gfx] [PATCH 11/20] drm/i915/gem: Separate the ww_mutex walker into its own list
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 11/20] drm/i915/gem: Separate the ww_mutex walker into its own list Chris Wilson
@ 2020-07-13 14:53   ` Tvrtko Ursulin
  2020-07-14 14:10     ` Chris Wilson
  0 siblings, 1 reply; 65+ messages in thread
From: Tvrtko Ursulin @ 2020-07-13 14:53 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 06/07/2020 07:19, Chris Wilson wrote:
> In preparation for making eb_vma bigger and heavy to run inn parallel,

in

> we need to stop apply an in-place swap() to reorder around ww_mutex

applying

> deadlocks. Keep the array intact and reorder the locks using a dedicated
> list.
> 
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> ---
>   .../gpu/drm/i915/gem/i915_gem_execbuffer.c    | 83 ++++++++++++-------
>   1 file changed, 54 insertions(+), 29 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> index 6d20be29ff3c..4d8ac89c56fc 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> @@ -37,6 +37,7 @@ struct eb_vma {
>   	struct list_head bind_link;
>   	struct list_head unbound_link;
>   	struct list_head reloc_link;
> +	struct list_head submit_link;
>   
>   	struct hlist_node node;
>   	u32 handle;
> @@ -248,6 +249,8 @@ struct i915_execbuffer {
>   	/** list of vma that have execobj.relocation_count */
>   	struct list_head relocs;
>   
> +	struct list_head submit_list;
> +
>   	/**
>   	 * Track the most recently used object for relocations, as we
>   	 * frequently have to perform multiple relocations within the same
> @@ -341,6 +344,42 @@ static void eb_vma_array_put(struct eb_vma_array *arr)
>   	kref_put(&arr->kref, eb_vma_array_destroy);
>   }
>   
> +static int
> +eb_lock_vma(struct i915_execbuffer *eb, struct ww_acquire_ctx *acquire)

vmas plural?

> +{
> +	struct eb_vma *ev;
> +	int err = 0;
> +
> +	list_for_each_entry(ev, &eb->submit_list, submit_link) {
> +		struct i915_vma *vma = ev->vma;
> +
> +		err = ww_mutex_lock_interruptible(&vma->resv->lock, acquire);
> +		if (err == -EDEADLK) {
> +			struct eb_vma *unlock = ev, *en;
> +
> +			list_for_each_entry_safe_continue_reverse(unlock, en,
> +								  &eb->submit_list,
> +								  submit_link) {
> +				ww_mutex_unlock(&unlock->vma->resv->lock);
> +				list_move_tail(&unlock->submit_link, &eb->submit_list);
> +			}
> +
> +			GEM_BUG_ON(!list_is_first(&ev->submit_link, &eb->submit_list));
> +			err = ww_mutex_lock_slow_interruptible(&vma->resv->lock,
> +							       acquire);
> +		}
> +		if (err) {
> +			list_for_each_entry_continue_reverse(ev,
> +							     &eb->submit_list,
> +							     submit_link)
> +				ww_mutex_unlock(&ev->vma->resv->lock);
> +			break;
> +		}
> +	}
> +
> +	return err;
> +}
> +
>   static int eb_create(struct i915_execbuffer *eb)
>   {
>   	/* Allocate an extra slot for use by the command parser + sentinel */
> @@ -393,6 +432,10 @@ static int eb_create(struct i915_execbuffer *eb)
>   		eb->lut_size = -eb->buffer_count;
>   	}
>   
> +	INIT_LIST_HEAD(&eb->bind_list);
> +	INIT_LIST_HEAD(&eb->submit_list);
> +	INIT_LIST_HEAD(&eb->relocs);
> +
>   	return 0;
>   }
>   
> @@ -574,6 +617,7 @@ eb_add_vma(struct i915_execbuffer *eb,
>   	}
>   
>   	list_add_tail(&ev->bind_link, &eb->bind_list);
> +	list_add_tail(&ev->submit_link, &eb->submit_list);
>   
>   	if (entry->relocation_count)
>   		list_add_tail(&ev->reloc_link, &eb->relocs);
> @@ -910,9 +954,6 @@ static int eb_lookup_vmas(struct i915_execbuffer *eb)
>   	unsigned int i;
>   	int err = 0;
>   
> -	INIT_LIST_HEAD(&eb->bind_list);
> -	INIT_LIST_HEAD(&eb->relocs);
> -
>   	for (i = 0; i < eb->buffer_count; i++) {
>   		struct i915_vma *vma;
>   
> @@ -1583,38 +1624,19 @@ static int eb_relocate(struct i915_execbuffer *eb)
>   
>   static int eb_move_to_gpu(struct i915_execbuffer *eb)
>   {
> -	const unsigned int count = eb->buffer_count;
>   	struct ww_acquire_ctx acquire;
> -	unsigned int i;
> +	struct eb_vma *ev;
>   	int err = 0;
>   
>   	ww_acquire_init(&acquire, &reservation_ww_class);
>   
> -	for (i = 0; i < count; i++) {
> -		struct eb_vma *ev = &eb->vma[i];
> -		struct i915_vma *vma = ev->vma;
> -
> -		err = ww_mutex_lock_interruptible(&vma->resv->lock, &acquire);
> -		if (err == -EDEADLK) {
> -			GEM_BUG_ON(i == 0);
> -			do {
> -				int j = i - 1;
> -
> -				ww_mutex_unlock(&eb->vma[j].vma->resv->lock);
> -
> -				swap(eb->vma[i],  eb->vma[j]);
> -			} while (--i);
> +	err = eb_lock_vma(eb, &acquire);
> +	if (err)
> +		goto err_fini;
>   
> -			err = ww_mutex_lock_slow_interruptible(&vma->resv->lock,
> -							       &acquire);
> -		}
> -		if (err)
> -			break;
> -	}
>   	ww_acquire_done(&acquire);
>   
> -	while (i--) {
> -		struct eb_vma *ev = &eb->vma[i];
> +	list_for_each_entry(ev, &eb->submit_list, submit_link) {
>   		struct i915_vma *vma = ev->vma;
>   		unsigned int flags = ev->flags;
>   		struct drm_i915_gem_object *obj = vma->obj;
> @@ -1671,6 +1693,8 @@ static int eb_move_to_gpu(struct i915_execbuffer *eb)
>   	intel_gt_chipset_flush(eb->engine->gt);
>   	return 0;
>   
> +err_fini:
> +	ww_acquire_fini(&acquire);
>   err_skip:
>   	i915_request_set_error_once(eb->request, err);
>   	return err;
> @@ -1952,9 +1976,10 @@ static int eb_parse(struct i915_execbuffer *eb)
>   	if (err)
>   		goto err_trampoline;
>   
> -	eb->vma[eb->buffer_count].vma = i915_vma_get(shadow);
> -	eb->vma[eb->buffer_count].flags = __EXEC_OBJECT_HAS_PIN;
>   	eb->batch = &eb->vma[eb->buffer_count++];
> +	eb->batch->vma = i915_vma_get(shadow);
> +	eb->batch->flags = __EXEC_OBJECT_HAS_PIN;
> +	list_add_tail(&eb->batch->submit_link, &eb->submit_list);
>   	eb->vma[eb->buffer_count].vma = NULL;
>   
>   	eb->trampoline = trampoline;
> 

Just a temporary stage... are we reviewing those? Best if they can be 
avoided.

Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>

Regards,

Tvrtko
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Intel-gfx] [PATCH 12/20] drm/i915/gem: Asynchronous GTT unbinding
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 12/20] drm/i915/gem: Asynchronous GTT unbinding Chris Wilson
@ 2020-07-14  9:02   ` Tvrtko Ursulin
  2020-07-14 15:05     ` Chris Wilson
  0 siblings, 1 reply; 65+ messages in thread
From: Tvrtko Ursulin @ 2020-07-14  9:02 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx; +Cc: Matthew Auld


On 06/07/2020 07:19, Chris Wilson wrote:
> It is reasonably common for userspace (even modern drivers like iris) to
> reuse an active address for a new buffer. This would cause the
> application to stall under its mutex (originally struct_mutex) until the
> old batches were idle and it could synchronously remove the stale PTE.
> However, we can queue up a job that waits on the signal for the old
> nodes to complete and upon those signals, remove the old nodes replacing
> them with the new ones for the batch. This is still CPU driven, but in
> theory we can do the GTT patching from the GPU. The job itself has a
> completion signal allowing the execbuf to wait upon the rebinding, and
> also other observers to coordinate with the common VM activity.
> 
> Letting userspace queue up more work, lets it do more stuff without
> blocking other clients. In turn, we take care not to let it too much
> concurrent work, creating a small number of queues for each context to
> limit the number of concurrent tasks.

This is a monster patch.. what is the end result here? If there are a 
few conflicts they can go async, but if more than "concurrency width" 
need evict then it will be synchronous?

Could you do without this patch for the first implementation? Or come up 
with ideas to split it up and so make understanding and review manageable?

> 
> The implementation relies on only scheduling one unbind operation per
> vma as we use the unbound vma->node location to track the stale PTE.
> 
> Closes: https://gitlab.freedesktop.org/drm/intel/issues/1402
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> Cc: Matthew Auld <matthew.auld@intel.com>
> Cc: Andi Shyti <andi.shyti@intel.com>
> ---
>   .../gpu/drm/i915/gem/i915_gem_execbuffer.c    | 917 ++++++++++++++++--
>   drivers/gpu/drm/i915/gt/gen6_ppgtt.c          |   1 +
>   drivers/gpu/drm/i915/gt/intel_gtt.c           |   4 +
>   drivers/gpu/drm/i915/gt/intel_gtt.h           |   2 +
>   drivers/gpu/drm/i915/i915_gem.c               |   7 +
>   drivers/gpu/drm/i915/i915_gem_gtt.c           |   5 +
>   drivers/gpu/drm/i915/i915_vma.c               |  71 +-
>   drivers/gpu/drm/i915/i915_vma.h               |   4 +
>   8 files changed, 883 insertions(+), 128 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> index 4d8ac89c56fc..6a406e8798ef 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> @@ -18,6 +18,7 @@
>   #include "gt/intel_gt.h"
>   #include "gt/intel_gt_buffer_pool.h"
>   #include "gt/intel_gt_pm.h"
> +#include "gt/intel_gt_requests.h"
>   #include "gt/intel_ring.h"
>   
>   #include "i915_drv.h"
> @@ -43,6 +44,12 @@ struct eb_vma {
>   	u32 handle;
>   };
>   
> +struct eb_bind_vma {
> +	struct eb_vma *ev;
> +	struct drm_mm_node hole;
> +	unsigned int bind_flags;
> +};
> +
>   struct eb_vma_array {
>   	struct kref kref;
>   	struct eb_vma vma[];
> @@ -66,11 +73,12 @@ struct eb_vma_array {
>   	 I915_EXEC_RESOURCE_STREAMER)
>   
>   /* Catch emission of unexpected errors for CI! */
> +#define __EINVAL__ 22
>   #if IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM)
>   #undef EINVAL
>   #define EINVAL ({ \
>   	DRM_DEBUG_DRIVER("EINVAL at %s:%d\n", __func__, __LINE__); \
> -	22; \
> +	__EINVAL__; \
>   })
>   #endif
>   
> @@ -311,6 +319,12 @@ static struct eb_vma_array *eb_vma_array_create(unsigned int count)
>   	return arr;
>   }
>   
> +static struct eb_vma_array *eb_vma_array_get(struct eb_vma_array *arr)
> +{
> +	kref_get(&arr->kref);
> +	return arr;
> +}
> +
>   static inline void eb_unreserve_vma(struct eb_vma *ev)
>   {
>   	struct i915_vma *vma = ev->vma;
> @@ -444,7 +458,10 @@ eb_vma_misplaced(const struct drm_i915_gem_exec_object2 *entry,
>   		 const struct i915_vma *vma,
>   		 unsigned int flags)
>   {
> -	if (vma->node.size < entry->pad_to_size)
> +	if (test_bit(I915_VMA_ERROR_BIT, __i915_vma_flags(vma)))
> +		return true;
> +
> +	if (vma->node.size < max(vma->size, entry->pad_to_size))
>   		return true;
>   
>   	if (entry->alignment && !IS_ALIGNED(vma->node.start, entry->alignment))
> @@ -469,32 +486,6 @@ eb_vma_misplaced(const struct drm_i915_gem_exec_object2 *entry,
>   	return false;
>   }
>   
> -static u64 eb_pin_flags(const struct drm_i915_gem_exec_object2 *entry,
> -			unsigned int exec_flags)
> -{
> -	u64 pin_flags = 0;
> -
> -	if (exec_flags & EXEC_OBJECT_NEEDS_GTT)
> -		pin_flags |= PIN_GLOBAL;
> -
> -	/*
> -	 * Wa32bitGeneralStateOffset & Wa32bitInstructionBaseOffset,
> -	 * limit address to the first 4GBs for unflagged objects.
> -	 */
> -	if (!(exec_flags & EXEC_OBJECT_SUPPORTS_48B_ADDRESS))
> -		pin_flags |= PIN_ZONE_4G;
> -
> -	if (exec_flags & __EXEC_OBJECT_NEEDS_MAP)
> -		pin_flags |= PIN_MAPPABLE;
> -
> -	if (exec_flags & EXEC_OBJECT_PINNED)
> -		pin_flags |= entry->offset | PIN_OFFSET_FIXED;
> -	else if (exec_flags & __EXEC_OBJECT_NEEDS_BIAS)
> -		pin_flags |= BATCH_OFFSET_BIAS | PIN_OFFSET_BIAS;
> -
> -	return pin_flags;
> -}
> -
>   static bool eb_pin_vma_fence_inplace(struct eb_vma *ev)
>   {
>   	struct i915_vma *vma = ev->vma;
> @@ -522,6 +513,10 @@ eb_pin_vma_inplace(struct i915_execbuffer *eb,
>   	struct i915_vma *vma = ev->vma;
>   	unsigned int pin_flags;
>   
> +	/* Concurrent async binds in progress, get in the queue */
> +	if (!i915_active_is_idle(&vma->vm->binding))
> +		return false;
> +
>   	if (eb_vma_misplaced(entry, vma, ev->flags))
>   		return false;
>   
> @@ -642,45 +637,463 @@ eb_add_vma(struct i915_execbuffer *eb,
>   	}
>   }
>   
> -static int eb_reserve_vma(const struct i915_execbuffer *eb,
> -			  struct eb_vma *ev,
> -			  u64 pin_flags)
> +struct eb_vm_work {
> +	struct dma_fence_work base;
> +	struct eb_vma_array *array;
> +	struct eb_bind_vma *bind;
> +	struct i915_address_space *vm;
> +	struct i915_vm_pt_stash stash;
> +	struct list_head evict_list;
> +	u64 *p_flags;
> +	u64 id;
> +	unsigned long count;
> +};
> +
> +static inline u64 node_end(const struct drm_mm_node *node)
> +{
> +	return node->start + node->size;
> +}
> +
> +static int set_bind_fence(struct i915_vma *vma, struct eb_vm_work *work)
> +{
> +	struct dma_fence *prev;
> +	int err = 0;
> +
> +	lockdep_assert_held(&vma->vm->mutex);
> +	prev = i915_active_set_exclusive(&vma->active, &work->base.dma);
> +	if (unlikely(prev)) {
> +		err = i915_sw_fence_await_dma_fence(&work->base.chain, prev, 0,
> +						    GFP_NOWAIT | __GFP_NOWARN);
> +		dma_fence_put(prev);
> +	}
> +
> +	return err < 0 ? err : 0;
> +}
> +
> +static int await_evict(struct eb_vm_work *work, struct i915_vma *vma)
>   {
> -	struct drm_i915_gem_exec_object2 *entry = ev->exec;
> -	struct i915_vma *vma = ev->vma;
>   	int err;
>   
> -	if (drm_mm_node_allocated(&vma->node) &&
> -	    eb_vma_misplaced(entry, vma, ev->flags)) {
> -		err = i915_vma_unbind(vma);
> +	GEM_BUG_ON(rcu_access_pointer(vma->active.excl.fence) == &work->base.dma);
> +
> +	/* Wait for all other previous activity */
> +	err = i915_sw_fence_await_active(&work->base.chain,
> +					 &vma->active,
> +					 I915_ACTIVE_AWAIT_ACTIVE);
> +	/* Then insert along the exclusive vm->mutex timeline */
> +	if (err == 0)
> +		err = set_bind_fence(vma, work);
> +
> +	return err;
> +}
> +
> +static int
> +evict_for_node(struct eb_vm_work *work,
> +	       struct eb_bind_vma *const target,
> +	       unsigned int flags)
> +{
> +	struct i915_vma *target_vma = target->ev->vma;
> +	struct i915_address_space *vm = target_vma->vm;
> +	const unsigned long color = target_vma->node.color;
> +	const u64 start = target_vma->node.start;
> +	const u64 end = start + target_vma->node.size;
> +	u64 hole_start = start, hole_end = end;
> +	struct i915_vma *vma, *next;
> +	struct drm_mm_node *node;
> +	LIST_HEAD(evict_list);
> +	LIST_HEAD(steal_list);
> +	int err = 0;
> +
> +	lockdep_assert_held(&vm->mutex);
> +	GEM_BUG_ON(drm_mm_node_allocated(&target_vma->node));
> +	GEM_BUG_ON(!IS_ALIGNED(start, I915_GTT_PAGE_SIZE));
> +	GEM_BUG_ON(!IS_ALIGNED(end, I915_GTT_PAGE_SIZE));
> +
> +	if (i915_vm_has_cache_coloring(vm)) {
> +		/* Expand search to cover neighbouring guard pages (or lack!) */
> +		if (hole_start)
> +			hole_start -= I915_GTT_PAGE_SIZE;
> +
> +		/* Always look at the page afterwards to avoid the end-of-GTT */
> +		hole_end += I915_GTT_PAGE_SIZE;
> +	}
> +	GEM_BUG_ON(hole_start >= hole_end);
> +
> +	drm_mm_for_each_node_in_range(node, &vm->mm, hole_start, hole_end) {
> +		GEM_BUG_ON(node == &target_vma->node);
> +		err = -ENOSPC;
> +
> +		/* If we find any non-objects (!vma), we cannot evict them */
> +		if (node->color == I915_COLOR_UNEVICTABLE)
> +			goto err;
> +
> +		/*
> +		 * If we are using coloring to insert guard pages between
> +		 * different cache domains within the address space, we have
> +		 * to check whether the objects on either side of our range
> +		 * abutt and conflict. If they are in conflict, then we evict
> +		 * those as well to make room for our guard pages.
> +		 */
> +		if (i915_vm_has_cache_coloring(vm)) {
> +			if (node_end(node) == start && node->color == color)
> +				continue;
> +
> +			if (node->start == end && node->color == color)
> +				continue;
> +		}
> +
> +		GEM_BUG_ON(!drm_mm_node_allocated(node));
> +		vma = container_of(node, typeof(*vma), node);
> +
> +		if (flags & PIN_NOEVICT || i915_vma_is_pinned(vma))
> +			goto err;
> +
> +		/* If this VMA is already being freed, or idle, steal it! */
> +		if (!i915_active_acquire_if_busy(&vma->active)) {
> +			list_move(&vma->vm_link, &steal_list);
> +			continue;
> +		}
> +
> +		if (!(flags & PIN_NONBLOCK))
> +			err = await_evict(work, vma);
> +		i915_active_release(&vma->active);
>   		if (err)
> -			return err;
> +			goto err;
> +
> +		GEM_BUG_ON(!i915_vma_is_active(vma));
> +		list_move(&vma->vm_link, &evict_list);
>   	}
>   
> -	err = i915_vma_pin(vma,
> -			   entry->pad_to_size, entry->alignment,
> -			   eb_pin_flags(entry, ev->flags) | pin_flags);
> -	if (err)
> -		return err;
> +	list_for_each_entry_safe(vma, next, &steal_list, vm_link) {
> +		GEM_BUG_ON(i915_vma_is_pinned(vma));
> +		GEM_BUG_ON(i915_vma_is_active(vma));
> +		__i915_vma_evict(vma);
> +		drm_mm_remove_node(&vma->node);
> +		/* No ref held; vma may now be concurrently freed */
> +	}
>   
> -	if (entry->offset != vma->node.start) {
> -		entry->offset = vma->node.start | UPDATE;
> -		eb->args->flags |= __EXEC_HAS_RELOC;
> +	/* No overlapping nodes to evict, claim the slot for ourselves! */
> +	if (list_empty(&evict_list))
> +		return drm_mm_reserve_node(&vm->mm, &target_vma->node);
> +
> +	/*
> +	 * Mark this range as reserved.
> +	 *
> +	 * We have not yet removed the PTEs for the old evicted nodes, so
> +	 * must prevent this range from being reused for anything else. The
> +	 * PTE will be cleared when the range is idle (during the rebind
> +	 * phase in the worker).
> +	 */
> +	target->hole.color = I915_COLOR_UNEVICTABLE;
> +	target->hole.start = start;
> +	target->hole.size = end;
> +
> +	list_for_each_entry(vma, &evict_list, vm_link) {
> +		target->hole.start =
> +			min(target->hole.start, vma->node.start);
> +		target->hole.size =
> +			max(target->hole.size, node_end(&vma->node));
> +
> +		GEM_BUG_ON(!i915_vma_is_active(vma));
> +		GEM_BUG_ON(vma->node.mm != &vm->mm);
> +		set_bit(I915_VMA_ERROR_BIT, __i915_vma_flags(vma));
> +		drm_mm_remove_node(&vma->node);
> +		GEM_BUG_ON(i915_vma_is_pinned(vma));
>   	}
> +	list_splice(&evict_list, &work->evict_list);
>   
> -	if (unlikely(ev->flags & EXEC_OBJECT_NEEDS_FENCE)) {
> -		err = i915_vma_pin_fence(vma);
> -		if (unlikely(err)) {
> -			i915_vma_unpin(vma);
> -			return err;
> +	target->hole.size -= target->hole.start;
> +
> +	return drm_mm_reserve_node(&vm->mm, &target->hole);
> +
> +err:
> +	list_splice(&evict_list, &vm->bound_list);
> +	list_splice(&steal_list, &vm->bound_list);
> +	return err;
> +}
> +
> +static int
> +evict_in_range(struct eb_vm_work *work,
> +	       struct eb_bind_vma * const target,
> +	       u64 start, u64 end, u64 align)
> +{
> +	struct i915_vma *target_vma = target->ev->vma;
> +	struct i915_address_space *vm = target_vma->vm;
> +	struct i915_vma *active = NULL;
> +	struct i915_vma *vma, *next;
> +	struct drm_mm_scan scan;
> +	LIST_HEAD(evict_list);
> +	bool found = false;
> +
> +	lockdep_assert_held(&vm->mutex);
> +
> +	drm_mm_scan_init_with_range(&scan, &vm->mm,
> +				    target_vma->node.size,
> +				    align,
> +				    target_vma->node.color,
> +				    start, end,
> +				    DRM_MM_INSERT_BEST);
> +
> +	list_for_each_entry_safe(vma, next, &vm->bound_list, vm_link) {
> +		if (i915_vma_is_pinned(vma))
> +			continue;
> +
> +		if (vma == active)
> +			active = ERR_PTR(-EAGAIN);
> +
> +		/* Prefer to reuse idle nodes; push all active vma to the end */
> +		if (active != ERR_PTR(-EAGAIN) && i915_vma_is_active(vma)) {
> +			if (!active)
> +				active = vma;
> +
> +			list_move_tail(&vma->vm_link, &vm->bound_list);
> +			continue;
>   		}
>   
> +		list_move(&vma->vm_link, &evict_list);
> +		if (drm_mm_scan_add_block(&scan, &vma->node)) {
> +			target_vma->node.start =
> +				round_up(scan.hit_start, align);
> +			found = true;
> +			break;
> +		}
> +	}
> +
> +	list_for_each_entry(vma, &evict_list, vm_link)
> +		drm_mm_scan_remove_block(&scan, &vma->node);
> +	list_splice(&evict_list, &vm->bound_list);
> +	if (!found)
> +		return -ENOSPC;
> +
> +	return evict_for_node(work, target, 0);
> +}
> +
> +static u64 random_offset(u64 start, u64 end, u64 len, u64 align)
> +{
> +	u64 range, addr;
> +
> +	GEM_BUG_ON(range_overflows(start, len, end));
> +	GEM_BUG_ON(round_up(start, align) > round_down(end - len, align));
> +
> +	range = round_down(end - len, align) - round_up(start, align);
> +	if (range) {
> +		if (sizeof(unsigned long) == sizeof(u64)) {
> +			addr = get_random_long();
> +		} else {
> +			addr = get_random_int();
> +			if (range > U32_MAX) {
> +				addr <<= 32;
> +				addr |= get_random_int();
> +			}
> +		}
> +		div64_u64_rem(addr, range, &addr);
> +		start += addr;
> +	}
> +
> +	return round_up(start, align);
> +}
> +
> +static u64 align0(u64 align)
> +{
> +	return align <= I915_GTT_MIN_ALIGNMENT ? 0 : align;
> +}
> +
> +static struct drm_mm_node *__best_hole(struct drm_mm *mm, u64 size)
> +{
> +	struct rb_node *rb = mm->holes_size.rb_root.rb_node;
> +	struct drm_mm_node *best = NULL;
> +
> +	while (rb) {
> +		struct drm_mm_node *node =
> +			rb_entry(rb, struct drm_mm_node, rb_hole_size);
> +
> +		if (size <= node->hole_size) {
> +			best = node;
> +			rb = rb->rb_right;
> +		} else {
> +			rb = rb->rb_left;
> +		}
> +	}
> +
> +	return best;
> +}
> +
> +static int best_hole(struct drm_mm *mm, struct drm_mm_node *node,
> +		     u64 start, u64 end, u64 align)
> +{
> +	struct drm_mm_node *hole;
> +	u64 size = node->size;
> +
> +	do {
> +		hole = __best_hole(mm, size);
> +		if (!hole)
> +			return -ENOSPC;
> +
> +		node->start = round_up(max(start, drm_mm_hole_node_start(hole)),
> +				       align);
> +		if (min(drm_mm_hole_node_end(hole), end) >=
> +		    node->start + node->size)
> +			return drm_mm_reserve_node(mm, node);
> +
> +		/*
> +		 * Too expensive to search for every single hole every time,
> +		 * so just look for the next bigger hole, introducing enough
> +		 * space for alignments. Finding the smallest hole with ideal
> +		 * alignment scales very poorly, so we choose to waste space
> +		 * if an alignment is forced. On the other hand, simply
> +		 * randomly selecting an offset in 48b space will cause us
> +		 * to use the majority of that space and exhaust all memory
> +		 * in storing the page directories. Compromise is required.
> +		 */
> +		size = hole->hole_size + align;
> +	} while (1);
> +}

evict_for_* and all above, feels like it is too much for 
i915_gem_execbuffer.c. How about that goes to i915_gem_evict.c? Apart 
from it depending on eb_vm_work..

Best hole at least operates solely on drm_mm so should go out, 
presumably in preparation for moving into drm core.

Hm, i915_gem_eb_vm.c for most of the rest?

> +
> +static int eb_reserve_vma(struct eb_vm_work *work, struct eb_bind_vma *bind)
> +{
> +	struct drm_i915_gem_exec_object2 *entry = bind->ev->exec;
> +	const unsigned int exec_flags = bind->ev->flags;
> +	struct i915_vma *vma = bind->ev->vma;
> +	struct i915_address_space *vm = vma->vm;
> +	u64 start = 0, end = vm->total;
> +	u64 align = entry->alignment ?: I915_GTT_MIN_ALIGNMENT;
> +	unsigned int bind_flags;
> +	int err;
> +
> +	lockdep_assert_held(&vm->mutex);
> +
> +	bind_flags = PIN_USER;
> +	if (exec_flags & EXEC_OBJECT_NEEDS_GTT)
> +		bind_flags |= PIN_GLOBAL;
> +
> +	if (drm_mm_node_allocated(&vma->node))
> +		goto pin;
> +
> +	GEM_BUG_ON(i915_vma_is_pinned(vma));
> +	GEM_BUG_ON(i915_vma_is_bound(vma, I915_VMA_BIND_MASK));
> +	GEM_BUG_ON(i915_active_fence_isset(&vma->active.excl));
> +	GEM_BUG_ON(!vma->size);
> +
> +	/* Reuse old address (if it doesn't conflict with new requirements) */
> +	if (eb_vma_misplaced(entry, vma, exec_flags)) {
> +		vma->node.start = entry->offset & PIN_OFFSET_MASK;
> +		vma->node.size = max(entry->pad_to_size, vma->size);
> +		vma->node.color = 0;
> +		if (i915_vm_has_cache_coloring(vm))
> +			vma->node.color = vma->obj->cache_level;
> +	}
> +
> +	/*
> +	 * Wa32bitGeneralStateOffset & Wa32bitInstructionBaseOffset,
> +	 * limit address to the first 4GBs for unflagged objects.
> +	 */
> +	if (!(exec_flags & EXEC_OBJECT_SUPPORTS_48B_ADDRESS))
> +		end = min_t(u64, end, (1ULL << 32) - I915_GTT_PAGE_SIZE);
> +
> +	align = max(align, vma->display_alignment);
> +	if (exec_flags & __EXEC_OBJECT_NEEDS_MAP) {
> +		vma->node.size = max_t(u64, vma->node.size, vma->fence_size);
> +		end = min_t(u64, end, i915_vm_to_ggtt(vm)->mappable_end);
> +		align = max_t(u64, align, vma->fence_alignment);
> +	}
> +
> +	if (exec_flags & __EXEC_OBJECT_NEEDS_BIAS)
> +		start = BATCH_OFFSET_BIAS;
> +
> +	GEM_BUG_ON(!vma->node.size);
> +	if (vma->node.size > end - start)
> +		return -E2BIG;
> +
> +	/* Try the user's preferred location first (mandatory if soft-pinned) */
> +	err = -__EINVAL__;
> +	if (vma->node.start >= start &&
> +	    IS_ALIGNED(vma->node.start, align) &&
> +	    !range_overflows(vma->node.start, vma->node.size, end)) {
> +		unsigned int pin_flags;
> +
> +		/*
> +		 * Prefer to relocate and spread objects around.
> +		 *
> +		 * If we relocate and continue to use the new location in
> +		 * future batches, we only pay the relocation cost once.
> +		 *
> +		 * If we instead keep reusing the same address for different
> +		 * objects, each batch must remove/insert objects into the GTT,
> +		 * which is more expensive than performing a relocation.
> +		 */
> +		pin_flags = 0;
> +		if (!(exec_flags & EXEC_OBJECT_PINNED))
> +			pin_flags = PIN_NOEVICT;
> +
> +		err = evict_for_node(work, bind, pin_flags);
> +		if (err == 0)
> +			goto pin;
> +	}
> +	if (exec_flags & EXEC_OBJECT_PINNED)
> +		return err;
> +
> +	/* Try the first available free space */
> +	if (!best_hole(&vm->mm, &vma->node, start, end, align))
> +		goto pin;
> +
> +	/* Pick a random slot and see if it's available [O(N) worst case] */
> +	vma->node.start = random_offset(start, end, vma->node.size, align);
> +	if (evict_for_node(work, bind, PIN_NONBLOCK) == 0)
> +		goto pin;
> +
> +	/* Otherwise search all free space [degrades to O(N^2)] */
> +	if (drm_mm_insert_node_in_range(&vm->mm, &vma->node,
> +					vma->node.size,
> +					align0(align),
> +					vma->node.color,
> +					start, end,
> +					DRM_MM_INSERT_BEST) == 0)
> +		goto pin;
> +
> +	/* Pretty busy! Loop over "LRU" and evict oldest in our search range */
> +	err = evict_in_range(work, bind, start, end, align);
> +	if (unlikely(err))
> +		return err;
> +
> +pin:
> +	if (unlikely(exec_flags & EXEC_OBJECT_NEEDS_FENCE)) {
> +		err = __i915_vma_pin_fence(vma); /* XXX no waiting */
> +		if (unlikely(err))
> +			return err;
> +
>   		if (vma->fence)
> -			ev->flags |= __EXEC_OBJECT_HAS_FENCE;
> +			bind->ev->flags |= __EXEC_OBJECT_HAS_FENCE;
>   	}
>   
> -	ev->flags |= __EXEC_OBJECT_HAS_PIN;
> -	GEM_BUG_ON(eb_vma_misplaced(entry, vma, ev->flags));
> +	bind_flags &= ~atomic_read(&vma->flags);
> +	if (bind_flags) {
> +		err = set_bind_fence(vma, work);
> +		if (unlikely(err))
> +			return err;
> +
> +		atomic_add(I915_VMA_PAGES_ACTIVE, &vma->pages_count);
> +		atomic_or(bind_flags, &vma->flags);
> +
> +		if (i915_vma_is_ggtt(vma))
> +			__i915_vma_set_map_and_fenceable(vma);
> +
> +		GEM_BUG_ON(!i915_vma_is_active(vma));
> +		list_move_tail(&vma->vm_link, &vm->bound_list);
> +		bind->bind_flags = bind_flags;
> +	}
> +	__i915_vma_pin(vma); /* and release */
> +
> +	GEM_BUG_ON(!bind_flags && !drm_mm_node_allocated(&vma->node));
> +	GEM_BUG_ON(!(drm_mm_node_allocated(&vma->node) ^
> +		     drm_mm_node_allocated(&bind->hole)));
> +
> +	if (entry->offset != vma->node.start) {
> +		entry->offset = vma->node.start | UPDATE;
> +		*work->p_flags |= __EXEC_HAS_RELOC;
> +	}
> +
> +	bind->ev->flags |= __EXEC_OBJECT_HAS_PIN;
> +	GEM_BUG_ON(eb_vma_misplaced(entry, vma, bind->ev->flags));
>   
>   	return 0;
>   }
> @@ -714,13 +1127,244 @@ static int wait_for_timeline(struct intel_timeline *tl)
>   	} while (1);
>   }
>   
> +static void __eb_bind_vma(struct eb_vm_work *work)
> +{
> +	struct i915_address_space *vm = work->vm;
> +	unsigned long n;
> +
> +	GEM_BUG_ON(!intel_gt_pm_is_awake(vm->gt));
> +
> +	/*
> +	 * We have to wait until the stale nodes are completely idle before
> +	 * we can remove their PTE and unbind their pages. Hence, after
> +	 * claiming their slot in the drm_mm, we defer their removal to
> +	 * after the fences are signaled.
> +	 */
> +	if (!list_empty(&work->evict_list)) {
> +		struct i915_vma *vma, *vn;
> +
> +		mutex_lock(&vm->mutex);
> +		list_for_each_entry_safe(vma, vn, &work->evict_list, vm_link) {
> +			GEM_BUG_ON(vma->vm != vm);
> +			__i915_vma_evict(vma);
> +			GEM_BUG_ON(!i915_vma_is_active(vma));
> +		}
> +		mutex_unlock(&vm->mutex);
> +	}
> +
> +	/*
> +	 * Now we know the nodes we require in drm_mm are idle, we can
> +	 * replace the PTE in those ranges with our own.
> +	 */
> +	for (n = 0; n < work->count; n++) {
> +		struct eb_bind_vma *bind = &work->bind[n];
> +		struct i915_vma *vma = bind->ev->vma;
> +
> +		if (!bind->bind_flags)
> +			goto put;
> +
> +		GEM_BUG_ON(vma->vm != vm);
> +		GEM_BUG_ON(!i915_vma_is_active(vma));
> +
> +		vma->ops->bind_vma(vm, &work->stash, vma,
> +				   vma->obj->cache_level, bind->bind_flags);
> +
> +		if (drm_mm_node_allocated(&bind->hole)) {
> +			mutex_lock(&vm->mutex);
> +			GEM_BUG_ON(bind->hole.mm != &vm->mm);
> +			GEM_BUG_ON(bind->hole.color != I915_COLOR_UNEVICTABLE);
> +			GEM_BUG_ON(drm_mm_node_allocated(&vma->node));
> +
> +			drm_mm_remove_node(&bind->hole);
> +			drm_mm_reserve_node(&vm->mm, &vma->node);
> +
> +			GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
> +			mutex_unlock(&vm->mutex);
> +		}
> +		bind->bind_flags = 0;
> +
> +put:
> +		GEM_BUG_ON(drm_mm_node_allocated(&bind->hole));
> +		i915_vma_put_pages(vma);
> +	}
> +	work->count = 0;
> +}
> +
> +static int eb_bind_vma(struct dma_fence_work *base)
> +{
> +	struct eb_vm_work *work = container_of(base, typeof(*work), base);
> +
> +	__eb_bind_vma(work);
> +	return 0;
> +}
> +
> +static void eb_vma_work_release(struct dma_fence_work *base)
> +{
> +	struct eb_vm_work *work = container_of(base, typeof(*work), base);
> +
> +	__eb_bind_vma(work);

Looks like on some early cancels this can try to cleanup things which 
haven't been set up yet, like the dereference of work->bind[n]. I don't 
see any protection against that.

Maybe it would be enough if work->count was set after the work has been 
set up.

Why is a callback called eb_vma_work_release doing doing the binds 
anyway? I'd expect release to clean up, not do the work.

> +	kvfree(work->bind);
> +
> +	if (work->id)
> +		i915_active_release(&work->vm->binding);
> +
> +	eb_vma_array_put(work->array);
> +
> +	i915_vm_free_pt_stash(work->vm, &work->stash);
> +	i915_vm_put(work->vm);
> +}
> +
> +static const struct dma_fence_work_ops eb_bind_ops = {
> +	.name = "eb_bind",
> +	.work = eb_bind_vma,
> +	.release = eb_vma_work_release,
> +};
> +
> +static int eb_vm_work_cancel(struct eb_vm_work *work, int err)
> +{
> +	work->base.dma.error = err;
> +	dma_fence_work_commit_imm(&work->base);
> +
> +	return err;
> +}
> +
> +static struct eb_vm_work *eb_vm_work(struct i915_execbuffer *eb,
> +				     unsigned long count)
> +{
> +	struct eb_vm_work *work;
> +
> +	work = kmalloc(sizeof(*work), GFP_KERNEL);
> +	if (!work)
> +		return NULL;
> +
> +	work->bind = kvmalloc(sizeof(*work->bind) * count, GFP_KERNEL);
> +	if (!work->bind) {
> +		kfree(work->bind);
> +		return NULL;
> +	}
> +	work->count = count;
> +
> +	INIT_LIST_HEAD(&work->evict_list);
> +
> +	dma_fence_work_init(&work->base, &eb_bind_ops);
> +	work->array = eb_vma_array_get(eb->array);
> +	work->p_flags = &eb->args->flags;

Why is the pointer shared?

> +	work->vm = i915_vm_get(eb->context->vm);
> +	memset(&work->stash, 0, sizeof(work->stash));
> +
> +	/* Preallocate our slot in vm->binding, outside of vm->mutex */
> +	work->id = i915_gem_context_async_id(eb->gem_context);
> +	if (i915_active_acquire_for_context(&work->vm->binding, work->id)) {
> +		work->id = 0;
> +		eb_vm_work_cancel(work, -ENOMEM);
> +		return NULL;
> +	}
> +
> +	return work;
> +}
> +
> +static int eb_vm_throttle(struct eb_vm_work *work)
> +{
> +	struct dma_fence *p;
> +	int err;
> +
> +	/* Keep async work queued per context */
> +	p = __i915_active_ref(&work->vm->binding, work->id, &work->base.dma);
> +	if (IS_ERR_OR_NULL(p))
> +		return PTR_ERR_OR_ZERO(p);
> +
> +	err = i915_sw_fence_await_dma_fence(&work->base.chain, p, 0,
> +					    GFP_NOWAIT | __GFP_NOWARN);
> +	dma_fence_put(p);
> +
> +	return err < 0 ? err : 0;
> +}
> +
> +static int eb_prepare_vma(struct eb_vm_work *work,
> +			  unsigned long idx,
> +			  struct eb_vma *ev)
> +{
> +	struct eb_bind_vma *bind = &work->bind[idx];
> +	struct i915_vma *vma = ev->vma;
> +	u64 max_size;
> +	int err;
> +
> +	bind->ev = ev;
> +	bind->hole.flags = 0;
> +	bind->bind_flags = 0;
> +
> +	/* Allocate enough page directories to cover worst case */
> +	max_size = max(vma->size, ev->exec->pad_to_size);
> +	if (ev->flags & __EXEC_OBJECT_NEEDS_MAP)
> +		max_size = max_t(u64, max_size, vma->fence_size);
> +
> +	err = i915_vm_alloc_pt_stash(work->vm, &work->stash, max_size);
> +	if (err)
> +		return err;
> +
> +	return i915_vma_get_pages(vma);
> +}
> +
> +static int wait_for_unbinds(struct i915_execbuffer *eb,
> +			    struct list_head *unbound,
> +			    int pass)
> +{
> +	struct eb_vma *ev;
> +	int err;
> +
> +	list_for_each_entry(ev, unbound, unbound_link) {
> +		struct i915_vma *vma = ev->vma;
> +
> +		GEM_BUG_ON(ev->flags & __EXEC_OBJECT_HAS_PIN);
> +
> +		if (drm_mm_node_allocated(&vma->node) &&
> +		    eb_vma_misplaced(ev->exec, vma, ev->flags)) {
> +			err = i915_vma_unbind(vma);
> +			if (err)
> +				return err;
> +		}
> +
> +		/* Wait for previous to avoid reusing vma->node */
> +		err = i915_vma_wait_for_unbind(vma);

This waits on vma->active, presumably the current execbuf hasn't added 
anything to it so far..

> +		if (err)
> +			return err;
> +	}
> +
> +	switch (pass) {
> +	default:
> +		return -ENOSPC;
> +
> +	case 2:
> +		/*
> +		 * Too fragmented, retire everything on the timeline and so
> +		 * make it all [contexts included] available to evict.
> +		 */
> +		err = wait_for_timeline(eb->context->timeline);
> +		if (err)
> +			return err;
> +
> +		fallthrough;
> +	case 1:
> +		/* XXX ticket lock */
> +		if (i915_active_wait(&eb->context->vm->binding))

.. and this waits on itself? Or?

> +			return -EINTR;
> +
> +		fallthrough;
> +	case 0:
> +		return 0;
> +	}
> +}
> +
>   static int eb_reserve_vm(struct i915_execbuffer *eb)
>   {
> -	unsigned int pin_flags = PIN_USER | PIN_NONBLOCK;
> +	struct i915_address_space *vm = eb->context->vm;
>   	struct list_head last, unbound;
> +	unsigned long count;
>   	struct eb_vma *ev;
>   	unsigned int pass;
> +	int err = 0;
>   
> +	count = 0;
>   	INIT_LIST_HEAD(&unbound);
>   	list_for_each_entry(ev, &eb->bind_list, bind_link) {
>   		struct drm_i915_gem_exec_object2 *entry = ev->exec;
> @@ -737,44 +1381,93 @@ static int eb_reserve_vm(struct i915_execbuffer *eb)
>   				list_add(&ev->unbound_link, &unbound);
>   			else
>   				list_add_tail(&ev->unbound_link, &unbound);
> +			count++;
>   		}
>   	}
> -
> -	if (list_empty(&unbound))
> +	if (count == 0)
>   		return 0;
>   
> -	/*
> -	 * Attempt to pin all of the buffers into the GTT.
> -	 * This is done in 3 phases:
> -	 *
> -	 * 1a. Unbind all objects that do not match the GTT constraints for
> -	 *     the execbuffer (fenceable, mappable, alignment etc).
> -	 * 1b. Increment pin count for already bound objects.
> -	 * 2.  Bind new objects.
> -	 * 3.  Decrement pin count.
> -	 *
> -	 * This avoid unnecessary unbinding of later objects in order to make
> -	 * room for the earlier objects *unless* we need to defragment.
> -	 */
> -
>   	pass = 0;
>   	do {
> -		int err = 0;
> +		struct eb_vm_work *work;
>   
> -		if (mutex_lock_interruptible(&eb->i915->drm.struct_mutex))
> -			return -EINTR;
> +		work = eb_vm_work(eb, count);
> +		if (!work)
> +			return -ENOMEM;
>   
> +		count = 0;
>   		list_for_each_entry(ev, &unbound, unbound_link) {
> -			err = eb_reserve_vma(eb, ev, pin_flags);
> +			err = eb_prepare_vma(work, count++, ev);
> +			if (err) {
> +				work->count = count - 1;
> +
> +				if (eb_vm_work_cancel(work, err) == -EAGAIN)
> +					goto retry;
> +
> +				return err;
> +			}
> +		}
> +
> +		err = i915_vm_pin_pt_stash(work->vm, &work->stash);
> +		if (err)
> +			return eb_vm_work_cancel(work, err);
> +
> +		/* No allocations allowed beyond this point */
> +		if (mutex_lock_interruptible(&vm->mutex))
> +			return eb_vm_work_cancel(work, -EINTR);
> +
> +		err = eb_vm_throttle(work);

The effect is installing an async await on the current binding work, 
with the signaler being what is currently in vm->binding. This could be 
another execbuf from the same client?

I give up for now. It's a lot of high level and low level changes for 
one patch.

Regards,

Tvrtko

> +		if (err) {
> +			mutex_unlock(&vm->mutex);
> +			return eb_vm_work_cancel(work, err);
> +		}
> +
> +		for (count = 0; count < work->count; count++) {
> +			struct eb_bind_vma *bind = &work->bind[count];
> +			struct i915_vma *vma;
> +
> +			ev = bind->ev;
> +			vma = ev->vma;
> +
> +			/*
> +			 * Check if this node is being evicted or must be.
> +			 *
> +			 * As we use the single node inside the vma to track
> +			 * both the eviction and where to insert the new node,
> +			 * we cannot handle migrating the vma inside the worker.
> +			 */
> +			if (drm_mm_node_allocated(&vma->node)) {
> +				if (eb_vma_misplaced(ev->exec, vma, ev->flags)) {
> +					err = -ENOSPC;
> +					break;
> +				}
> +			} else {
> +				if (i915_vma_is_active(vma)) {
> +					err = -ENOSPC;
> +					break;
> +				}
> +			}
> +
> +			err = i915_active_acquire(&vma->active);
> +			if (!err) {
> +				err = eb_reserve_vma(work, bind);
> +				i915_active_release(&vma->active);
> +			}
>   			if (err)
>   				break;
> +
> +			GEM_BUG_ON(!i915_vma_is_pinned(vma));
>   		}
> -		if (!(err == -ENOSPC || err == -EAGAIN)) {
> -			mutex_unlock(&eb->i915->drm.struct_mutex);
> +
> +		mutex_unlock(&vm->mutex);
> +
> +		dma_fence_work_commit_imm(&work->base);
> +		if (err != -ENOSPC)
>   			return err;
> -		}
>   
> +retry:
>   		/* Resort *all* the objects into priority order */
> +		count = 0;
>   		INIT_LIST_HEAD(&unbound);
>   		INIT_LIST_HEAD(&last);
>   		list_for_each_entry(ev, &eb->bind_list, bind_link) {
> @@ -785,6 +1478,7 @@ static int eb_reserve_vm(struct i915_execbuffer *eb)
>   				continue;
>   
>   			eb_unreserve_vma(ev);
> +			count++;
>   
>   			if (flags & EXEC_OBJECT_PINNED)
>   				/* Pinned must have their slot */
> @@ -799,34 +1493,21 @@ static int eb_reserve_vm(struct i915_execbuffer *eb)
>   				list_add_tail(&ev->unbound_link, &last);
>   		}
>   		list_splice_tail(&last, &unbound);
> -		mutex_unlock(&eb->i915->drm.struct_mutex);
> +		GEM_BUG_ON(!count);
>   
> -		if (err == -EAGAIN) {
> -			flush_workqueue(eb->i915->mm.userptr_wq);
> -			continue;
> -		}
> -
> -		switch (pass++) {
> -		case 0:
> -			break;
> -
> -		case 1:
> -			/*
> -			 * Too fragmented, retire everything on the timeline
> -			 * and so make it all [contexts included] available to
> -			 * evict.
> -			 */
> -			err = wait_for_timeline(eb->context->timeline);
> -			if (err)
> -				return err;
> +		if (signal_pending(current))
> +			return -EINTR;
>   
> -			break;
> +		/* Now safe to wait with no reservations held */
>   
> -		default:
> -			return -ENOSPC;
> +		if (err == -EAGAIN) {
> +			flush_workqueue(eb->i915->mm.userptr_wq);
> +			pass = 0;
>   		}
>   
> -		pin_flags = PIN_USER;
> +		err = wait_for_unbinds(eb, &unbound, pass++);
> +		if (err)
> +			return err;
>   	} while (1);
>   }
>   
> @@ -1418,6 +2099,29 @@ relocate_entry(struct i915_execbuffer *eb,
>   	return target->node.start | UPDATE;
>   }
>   
> +static int gen6_fixup_ggtt(struct i915_vma *vma)
> +{
> +	int err;
> +
> +	if (i915_vma_is_bound(vma, I915_VMA_GLOBAL_BIND))
> +		return 0;
> +
> +	err = i915_vma_wait_for_bind(vma);
> +	if (err)
> +		return err;
> +
> +	mutex_lock(&vma->vm->mutex);
> +	if (!(atomic_fetch_or(I915_VMA_GLOBAL_BIND, &vma->flags) & I915_VMA_GLOBAL_BIND)) {
> +		__i915_gem_object_pin_pages(vma->obj);
> +		vma->ops->bind_vma(vma->vm, NULL, vma,
> +				   vma->obj->cache_level,
> +				   I915_VMA_GLOBAL_BIND);
> +	}
> +	mutex_unlock(&vma->vm->mutex);
> +
> +	return 0;
> +}
> +
>   static u64
>   eb_relocate_entry(struct i915_execbuffer *eb,
>   		  struct eb_vma *ev,
> @@ -1432,6 +2136,8 @@ eb_relocate_entry(struct i915_execbuffer *eb,
>   	if (unlikely(!target))
>   		return -ENOENT;
>   
> +	GEM_BUG_ON(!i915_vma_is_pinned(target->vma));
> +
>   	/* Validate that the target is in a valid r/w GPU domain */
>   	if (unlikely(reloc->write_domain & (reloc->write_domain - 1))) {
>   		drm_dbg(&i915->drm, "reloc with multiple write domains: "
> @@ -1466,9 +2172,7 @@ eb_relocate_entry(struct i915_execbuffer *eb,
>   		 */
>   		if (reloc->write_domain == I915_GEM_DOMAIN_INSTRUCTION &&
>   		    IS_GEN(eb->i915, 6)) {
> -			err = i915_vma_bind(target->vma,
> -					    target->vma->obj->cache_level,
> -					    PIN_GLOBAL, NULL);
> +			err = gen6_fixup_ggtt(target->vma);
>   			if (err)
>   				return err;
>   		}
> @@ -1642,6 +2346,8 @@ static int eb_move_to_gpu(struct i915_execbuffer *eb)
>   		struct drm_i915_gem_object *obj = vma->obj;
>   
>   		assert_vma_held(vma);
> +		GEM_BUG_ON(!(flags & __EXEC_OBJECT_HAS_PIN));
> +		GEM_BUG_ON(!i915_vma_is_bound(vma, I915_VMA_LOCAL_BIND));
>   
>   		if (flags & EXEC_OBJECT_CAPTURE) {
>   			struct i915_capture_list *capture;
> @@ -1680,7 +2386,6 @@ static int eb_move_to_gpu(struct i915_execbuffer *eb)
>   			err = i915_vma_move_to_active(vma, eb->request, flags);
>   
>   		i915_vma_unlock(vma);
> -		eb_unreserve_vma(ev);
>   	}
>   	ww_acquire_fini(&acquire);
>   
> @@ -2623,7 +3328,7 @@ i915_gem_do_execbuffer(struct drm_device *dev,
>   	 * snb/ivb/vlv conflate the "batch in ppgtt" bit with the "non-secure
>   	 * batch" bit. Hence we need to pin secure batches into the global gtt.
>   	 * hsw should have this fixed, but bdw mucks it up again. */
> -	batch = eb.batch->vma;
> +	batch = i915_vma_get(eb.batch->vma);
>   	if (eb.batch_flags & I915_DISPATCH_SECURE) {
>   		struct i915_vma *vma;
>   
> @@ -2643,6 +3348,7 @@ i915_gem_do_execbuffer(struct drm_device *dev,
>   			goto err_parse;
>   		}
>   
> +		GEM_BUG_ON(vma->obj != batch->obj);
>   		batch = vma;
>   	}
>   
> @@ -2722,6 +3428,7 @@ i915_gem_do_execbuffer(struct drm_device *dev,
>   err_parse:
>   	if (batch->private)
>   		intel_gt_buffer_pool_put(batch->private);
> +	i915_vma_put(batch);
>   err_vma:
>   	if (eb.trampoline)
>   		i915_vma_unpin(eb.trampoline);
> diff --git a/drivers/gpu/drm/i915/gt/gen6_ppgtt.c b/drivers/gpu/drm/i915/gt/gen6_ppgtt.c
> index 1c1e807f674d..1bfc7b9101e7 100644
> --- a/drivers/gpu/drm/i915/gt/gen6_ppgtt.c
> +++ b/drivers/gpu/drm/i915/gt/gen6_ppgtt.c
> @@ -359,6 +359,7 @@ static struct i915_vma *pd_vma_create(struct gen6_ppgtt *ppgtt, int size)
>   	atomic_set(&vma->flags, I915_VMA_GGTT);
>   	vma->ggtt_view.type = I915_GGTT_VIEW_ROTATED; /* prevent fencing */
>   
> +	INIT_LIST_HEAD(&vma->vm_link);
>   	INIT_LIST_HEAD(&vma->obj_link);
>   	INIT_LIST_HEAD(&vma->closed_link);
>   
> diff --git a/drivers/gpu/drm/i915/gt/intel_gtt.c b/drivers/gpu/drm/i915/gt/intel_gtt.c
> index 5df35e5a8936..8c044e6b4880 100644
> --- a/drivers/gpu/drm/i915/gt/intel_gtt.c
> +++ b/drivers/gpu/drm/i915/gt/intel_gtt.c
> @@ -55,6 +55,8 @@ void __i915_vm_close(struct i915_address_space *vm)
>   
>   void i915_address_space_fini(struct i915_address_space *vm)
>   {
> +	i915_active_fini(&vm->binding);
> +
>   	drm_mm_takedown(&vm->mm);
>   	mutex_destroy(&vm->mutex);
>   }
> @@ -100,6 +102,8 @@ void i915_address_space_init(struct i915_address_space *vm, int subclass)
>   	drm_mm_init(&vm->mm, 0, vm->total);
>   	vm->mm.head_node.color = I915_COLOR_UNEVICTABLE;
>   
> +	i915_active_init(&vm->binding, NULL, NULL);
> +
>   	INIT_LIST_HEAD(&vm->bound_list);
>   }
>   
> diff --git a/drivers/gpu/drm/i915/gt/intel_gtt.h b/drivers/gpu/drm/i915/gt/intel_gtt.h
> index 6e1bedf5448b..7ed804027ba1 100644
> --- a/drivers/gpu/drm/i915/gt/intel_gtt.h
> +++ b/drivers/gpu/drm/i915/gt/intel_gtt.h
> @@ -250,6 +250,8 @@ struct i915_address_space {
>   	 */
>   	struct list_head bound_list;
>   
> +	struct i915_active binding;
> +
>   	/* Global GTT */
>   	bool is_ggtt:1;
>   
> diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
> index 9aa3066cb75d..e998f25f30a3 100644
> --- a/drivers/gpu/drm/i915/i915_gem.c
> +++ b/drivers/gpu/drm/i915/i915_gem.c
> @@ -997,6 +997,9 @@ i915_gem_object_ggtt_pin(struct drm_i915_gem_object *obj,
>   		return vma;
>   
>   	if (i915_vma_misplaced(vma, size, alignment, flags)) {
> +		if (flags & PIN_NOEVICT)
> +			return ERR_PTR(-ENOSPC);
> +
>   		if (flags & PIN_NONBLOCK) {
>   			if (i915_vma_is_pinned(vma) || i915_vma_is_active(vma))
>   				return ERR_PTR(-ENOSPC);
> @@ -1016,6 +1019,10 @@ i915_gem_object_ggtt_pin(struct drm_i915_gem_object *obj,
>   			return ERR_PTR(ret);
>   	}
>   
> +	if (flags & PIN_NONBLOCK &&
> +	    i915_active_fence_isset(&vma->active.excl))
> +		return ERR_PTR(-EAGAIN);
> +
>   	ret = i915_vma_pin(vma, size, alignment, flags | PIN_GLOBAL);
>   	if (ret)
>   		return ERR_PTR(ret);
> diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
> index cb43381b0d37..7e1225874b03 100644
> --- a/drivers/gpu/drm/i915/i915_gem_gtt.c
> +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
> @@ -219,6 +219,8 @@ int i915_gem_gtt_insert(struct i915_address_space *vm,
>   		mode = DRM_MM_INSERT_HIGHEST;
>   	if (flags & PIN_MAPPABLE)
>   		mode = DRM_MM_INSERT_LOW;
> +	if (flags & PIN_NOSEARCH)
> +		mode |= DRM_MM_INSERT_ONCE;
>   
>   	/* We only allocate in PAGE_SIZE/GTT_PAGE_SIZE (4096) chunks,
>   	 * so we know that we always have a minimum alignment of 4096.
> @@ -236,6 +238,9 @@ int i915_gem_gtt_insert(struct i915_address_space *vm,
>   	if (err != -ENOSPC)
>   		return err;
>   
> +	if (flags & PIN_NOSEARCH)
> +		return -ENOSPC;
> +
>   	if (mode & DRM_MM_INSERT_ONCE) {
>   		err = drm_mm_insert_node_in_range(&vm->mm, node,
>   						  size, alignment, color,
> diff --git a/drivers/gpu/drm/i915/i915_vma.c b/drivers/gpu/drm/i915/i915_vma.c
> index 0c9af30fc3d6..cbfb11310846 100644
> --- a/drivers/gpu/drm/i915/i915_vma.c
> +++ b/drivers/gpu/drm/i915/i915_vma.c
> @@ -133,6 +133,7 @@ vma_create(struct drm_i915_gem_object *obj,
>   		fs_reclaim_release(GFP_KERNEL);
>   	}
>   
> +	INIT_LIST_HEAD(&vma->vm_link);
>   	INIT_LIST_HEAD(&vma->closed_link);
>   
>   	if (view && view->type != I915_GGTT_VIEW_NORMAL) {
> @@ -341,25 +342,37 @@ struct i915_vma_work *i915_vma_work(void)
>   	return vw;
>   }
>   
> -int i915_vma_wait_for_bind(struct i915_vma *vma)
> +static int
> +__i915_vma_wait_excl(struct i915_vma *vma, bool bound, unsigned int flags)
>   {
> +	struct dma_fence *fence;
>   	int err = 0;
>   
> -	if (rcu_access_pointer(vma->active.excl.fence)) {
> -		struct dma_fence *fence;
> +	fence = i915_active_fence_get(&vma->active.excl);
> +	if (!fence)
> +		return 0;
>   
> -		rcu_read_lock();
> -		fence = dma_fence_get_rcu_safe(&vma->active.excl.fence);
> -		rcu_read_unlock();
> -		if (fence) {
> -			err = dma_fence_wait(fence, MAX_SCHEDULE_TIMEOUT);
> -			dma_fence_put(fence);
> -		}
> +	if (drm_mm_node_allocated(&vma->node) == bound) {
> +		if (flags & PIN_NOEVICT)
> +			err = -EBUSY;
> +		else
> +			err = dma_fence_wait(fence, true);
>   	}
>   
> +	dma_fence_put(fence);
>   	return err;
>   }
>   
> +int i915_vma_wait_for_bind(struct i915_vma *vma)
> +{
> +	return __i915_vma_wait_excl(vma, true, 0);
> +}
> +
> +int i915_vma_wait_for_unbind(struct i915_vma *vma)
> +{
> +	return __i915_vma_wait_excl(vma, false, 0);
> +}
> +
>   /**
>    * i915_vma_bind - Sets up PTEs for an VMA in it's corresponding address space.
>    * @vma: VMA to map
> @@ -624,8 +637,9 @@ i915_vma_insert(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
>   	u64 start, end;
>   	int ret;
>   
> -	GEM_BUG_ON(i915_vma_is_bound(vma, I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND));
> +	GEM_BUG_ON(i915_vma_is_bound(vma, I915_VMA_BIND_MASK));
>   	GEM_BUG_ON(drm_mm_node_allocated(&vma->node));
> +	GEM_BUG_ON(i915_active_fence_isset(&vma->active.excl));
>   
>   	size = max(size, vma->size);
>   	alignment = max(alignment, vma->display_alignment);
> @@ -721,7 +735,7 @@ i915_vma_insert(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
>   	GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
>   	GEM_BUG_ON(!i915_gem_valid_gtt_space(vma, color));
>   
> -	list_add_tail(&vma->vm_link, &vma->vm->bound_list);
> +	list_move_tail(&vma->vm_link, &vma->vm->bound_list);
>   
>   	return 0;
>   }
> @@ -729,15 +743,12 @@ i915_vma_insert(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
>   static void
>   i915_vma_detach(struct i915_vma *vma)
>   {
> -	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));
> -
>   	/*
>   	 * And finally now the object is completely decoupled from this
>   	 * vma, we can drop its hold on the backing storage and allow
>   	 * it to be reaped by the shrinker.
>   	 */
> -	list_del(&vma->vm_link);
> +	list_del_init(&vma->vm_link);
>   }
>   
>   bool i915_vma_pin_inplace(struct i915_vma *vma, unsigned int flags)
> @@ -785,7 +796,7 @@ bool i915_vma_pin_inplace(struct i915_vma *vma, unsigned int flags)
>   	return pinned;
>   }
>   
> -static int vma_get_pages(struct i915_vma *vma)
> +int i915_vma_get_pages(struct i915_vma *vma)
>   {
>   	int err = 0;
>   
> @@ -832,7 +843,7 @@ static void __vma_put_pages(struct i915_vma *vma, unsigned int count)
>   	mutex_unlock(&vma->pages_mutex);
>   }
>   
> -static void vma_put_pages(struct i915_vma *vma)
> +void i915_vma_put_pages(struct i915_vma *vma)
>   {
>   	if (atomic_add_unless(&vma->pages_count, -1, 1))
>   		return;
> @@ -849,9 +860,13 @@ static void vma_unbind_pages(struct i915_vma *vma)
>   	/* 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);
> +	if (count)
> +		__vma_put_pages(vma, count | count << I915_VMA_PAGES_BIAS);
> +}
>   
> -	__vma_put_pages(vma, count | count << I915_VMA_PAGES_BIAS);
> +static int __wait_for_unbind(struct i915_vma *vma, unsigned int flags)
> +{
> +	return __i915_vma_wait_excl(vma, false, flags);
>   }
>   
>   int i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
> @@ -870,13 +885,17 @@ int i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
>   	if (i915_vma_pin_inplace(vma, flags & I915_VMA_BIND_MASK))
>   		return 0;
>   
> -	err = vma_get_pages(vma);
> +	err = i915_vma_get_pages(vma);
>   	if (err)
>   		return err;
>   
>   	if (flags & PIN_GLOBAL)
>   		wakeref = intel_runtime_pm_get(&vma->vm->i915->runtime_pm);
>   
> +	err = __wait_for_unbind(vma, flags);
> +	if (err)
> +		goto err_rpm;
> +
>   	if (flags & vma->vm->bind_async_flags) {
>   		u64 max_size;
>   
> @@ -951,6 +970,10 @@ int i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
>   		goto err_unlock;
>   
>   	if (!(bound & I915_VMA_BIND_MASK)) {
> +		err = __wait_for_unbind(vma, flags);
> +		if (err)
> +			goto err_active;
> +
>   		err = i915_vma_insert(vma, size, alignment, flags);
>   		if (err)
>   			goto err_active;
> @@ -970,6 +993,7 @@ int i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
>   	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);
> +	GEM_BUG_ON(!i915_vma_is_active(vma));
>   
>   	__i915_vma_pin(vma);
>   	GEM_BUG_ON(!i915_vma_is_pinned(vma));
> @@ -991,7 +1015,7 @@ int i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
>   err_rpm:
>   	if (wakeref)
>   		intel_runtime_pm_put(&vma->vm->i915->runtime_pm, wakeref);
> -	vma_put_pages(vma);
> +	i915_vma_put_pages(vma);
>   	return err;
>   }
>   
> @@ -1095,6 +1119,7 @@ void i915_vma_release(struct kref *ref)
>   		GEM_BUG_ON(drm_mm_node_allocated(&vma->node));
>   	}
>   	GEM_BUG_ON(i915_vma_is_active(vma));
> +	GEM_BUG_ON(!list_empty(&vma->vm_link));
>   
>   	if (vma->obj) {
>   		struct drm_i915_gem_object *obj = vma->obj;
> @@ -1154,7 +1179,7 @@ static void __i915_vma_iounmap(struct i915_vma *vma)
>   {
>   	GEM_BUG_ON(i915_vma_is_pinned(vma));
>   
> -	if (vma->iomap == NULL)
> +	if (!vma->iomap)
>   		return;
>   
>   	io_mapping_unmap(vma->iomap);
> diff --git a/drivers/gpu/drm/i915/i915_vma.h b/drivers/gpu/drm/i915/i915_vma.h
> index 03fea54fd573..9a26e6cbe8cd 100644
> --- a/drivers/gpu/drm/i915/i915_vma.h
> +++ b/drivers/gpu/drm/i915/i915_vma.h
> @@ -236,6 +236,9 @@ static inline void i915_vma_unlock(struct i915_vma *vma)
>   	dma_resv_unlock(vma->resv);
>   }
>   
> +int i915_vma_get_pages(struct i915_vma *vma);
> +void i915_vma_put_pages(struct i915_vma *vma);
> +
>   bool i915_vma_pin_inplace(struct i915_vma *vma, unsigned int flags);
>   
>   int __must_check
> @@ -379,6 +382,7 @@ void i915_vma_make_shrinkable(struct i915_vma *vma);
>   void i915_vma_make_purgeable(struct i915_vma *vma);
>   
>   int i915_vma_wait_for_bind(struct i915_vma *vma);
> +int i915_vma_wait_for_unbind(struct i915_vma *vma);
>   
>   static inline int i915_vma_sync(struct i915_vma *vma)
>   {
> 
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Intel-gfx] [PATCH 13/20] drm/i915/gem: Bind the fence async for execbuf
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 13/20] drm/i915/gem: Bind the fence async for execbuf Chris Wilson
@ 2020-07-14 12:19   ` Tvrtko Ursulin
  2020-07-14 15:21     ` Chris Wilson
  0 siblings, 1 reply; 65+ messages in thread
From: Tvrtko Ursulin @ 2020-07-14 12:19 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 06/07/2020 07:19, Chris Wilson wrote:
> It is illegal to wait on an another vma while holding the vm->mutex, as
> that easily leads to ABBA deadlocks (we wait on a second vma that waits
> on us to release the vm->mutex). So while the vm->mutex exists, move the
> waiting outside of the lock into the async binding pipeline.
> 
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> ---
>   .../gpu/drm/i915/gem/i915_gem_execbuffer.c    |  21 +--
>   drivers/gpu/drm/i915/gt/intel_ggtt_fencing.c  | 137 +++++++++++++++++-
>   drivers/gpu/drm/i915/gt/intel_ggtt_fencing.h  |   5 +
>   3 files changed, 151 insertions(+), 12 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> index 6a406e8798ef..c14c3b7e0dfd 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> @@ -1056,15 +1056,6 @@ static int eb_reserve_vma(struct eb_vm_work *work, struct eb_bind_vma *bind)
>   		return err;
>   
>   pin:
> -	if (unlikely(exec_flags & EXEC_OBJECT_NEEDS_FENCE)) {
> -		err = __i915_vma_pin_fence(vma); /* XXX no waiting */
> -		if (unlikely(err))
> -			return err;
> -
> -		if (vma->fence)
> -			bind->ev->flags |= __EXEC_OBJECT_HAS_FENCE;
> -	}
> -
>   	bind_flags &= ~atomic_read(&vma->flags);
>   	if (bind_flags) {
>   		err = set_bind_fence(vma, work);
> @@ -1095,6 +1086,15 @@ static int eb_reserve_vma(struct eb_vm_work *work, struct eb_bind_vma *bind)
>   	bind->ev->flags |= __EXEC_OBJECT_HAS_PIN;
>   	GEM_BUG_ON(eb_vma_misplaced(entry, vma, bind->ev->flags));
>   
> +	if (unlikely(exec_flags & EXEC_OBJECT_NEEDS_FENCE)) {
> +		err = __i915_vma_pin_fence_async(vma, &work->base);
> +		if (unlikely(err))
> +			return err;
> +
> +		if (vma->fence)
> +			bind->ev->flags |= __EXEC_OBJECT_HAS_FENCE;
> +	}
> +
>   	return 0;
>   }
>   
> @@ -1160,6 +1160,9 @@ static void __eb_bind_vma(struct eb_vm_work *work)
>   		struct eb_bind_vma *bind = &work->bind[n];
>   		struct i915_vma *vma = bind->ev->vma;
>   
> +		if (bind->ev->flags & __EXEC_OBJECT_HAS_FENCE)
> +			__i915_vma_apply_fence_async(vma);
> +
>   		if (!bind->bind_flags)
>   			goto put;
>   
> diff --git a/drivers/gpu/drm/i915/gt/intel_ggtt_fencing.c b/drivers/gpu/drm/i915/gt/intel_ggtt_fencing.c
> index 7fb36b12fe7a..734b6aa61809 100644
> --- a/drivers/gpu/drm/i915/gt/intel_ggtt_fencing.c
> +++ b/drivers/gpu/drm/i915/gt/intel_ggtt_fencing.c
> @@ -21,10 +21,13 @@
>    * IN THE SOFTWARE.
>    */
>   
> +#include "i915_active.h"
>   #include "i915_drv.h"
>   #include "i915_scatterlist.h"
> +#include "i915_sw_fence_work.h"
>   #include "i915_pvinfo.h"
>   #include "i915_vgpu.h"
> +#include "i915_vma.h"
>   
>   /**
>    * DOC: fence register handling
> @@ -340,19 +343,37 @@ static struct i915_fence_reg *fence_find(struct i915_ggtt *ggtt)
>   	return ERR_PTR(-EDEADLK);
>   }
>   
> +static int fence_wait_bind(struct i915_fence_reg *reg)
> +{
> +	struct dma_fence *fence;
> +	int err = 0;
> +
> +	fence = i915_active_fence_get(&reg->active.excl);
> +	if (fence) {
> +		err = dma_fence_wait(fence, true);
> +		dma_fence_put(fence);
> +	}
> +
> +	return err;
> +}
> +
>   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_fence_reg *fence = vma->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;
> +	if (fence) {
>   		GEM_BUG_ON(fence->vma != vma);
> +
> +		err = fence_wait_bind(fence);
> +		if (err)
> +			return err;
> +
>   		atomic_inc(&fence->pin_count);
>   		if (!fence->dirty) {
>   			list_move_tail(&fence->link, &ggtt->fence_list);
> @@ -384,6 +405,116 @@ int __i915_vma_pin_fence(struct i915_vma *vma)
>   	return err;
>   }
>   
> +static int set_bind_fence(struct i915_fence_reg *fence,
> +			  struct dma_fence_work *work)
> +{
> +	struct dma_fence *prev;
> +	int err;
> +
> +	if (rcu_access_pointer(fence->active.excl.fence) == &work->dma)
> +		return 0;

What is this checking for?

> +
> +	err = i915_sw_fence_await_active(&work->chain,
> +					 &fence->active,
> +					 I915_ACTIVE_AWAIT_ACTIVE);
> +	if (err)
> +		return err;
> +
> +	if (i915_active_acquire(&fence->active))
> +		return -ENOENT;
> +
> +	prev = i915_active_set_exclusive(&fence->active, &work->dma);
> +	if (unlikely(prev)) {
> +		err = i915_sw_fence_await_dma_fence(&work->chain, prev, 0,
> +						    GFP_NOWAIT | __GFP_NOWARN);

This is a potential allocation under vm->mutex.

> +		dma_fence_put(prev);
> +	}
> +
> +	i915_active_release(&fence->active);
> +	return err < 0 ? err : 0;
> +}
> +
> +int __i915_vma_pin_fence_async(struct i915_vma *vma,
> +			       struct dma_fence_work *work)
> +{
> +	struct i915_ggtt *ggtt = i915_vm_to_ggtt(vma->vm);
> +	struct i915_vma *set = i915_gem_object_is_tiled(vma->obj) ? vma : NULL;
> +	struct i915_fence_reg *fence = vma->fence;
> +	int err;
> +
> +	lockdep_assert_held(&vma->vm->mutex);
> +
> +	/* Just update our place in the LRU if our fence is getting reused. */
> +	if (fence) {
> +		GEM_BUG_ON(fence->vma != vma);
> +		GEM_BUG_ON(!i915_vma_is_map_and_fenceable(vma));
> +	} else if (set) {
> +		if (!i915_vma_is_map_and_fenceable(vma))
> +			return -EINVAL;
> +
> +		fence = fence_find(ggtt);
> +		if (IS_ERR(fence))
> +			return -ENOSPC;
> +
> +		GEM_BUG_ON(atomic_read(&fence->pin_count));
> +		fence->dirty = true;
> +	} else {
> +		return 0;
> +	}
> +
> +	atomic_inc(&fence->pin_count);
> +	list_move_tail(&fence->link, &ggtt->fence_list);
> +	if (!fence->dirty)
> +		return 0;
> +
> +	if (INTEL_GEN(fence_to_i915(fence)) < 4 &&
> +	    rcu_access_pointer(vma->active.excl.fence) != &work->dma) {

This second part is the same check as in set_bind_fence.

Should it go to a helper with a self-descriptive name and then this 
function would gate both the gen > 4 check and the set_bind_fence call 
under it?

> +		/* implicit 'unfenced' GPU blits */
> +		err = i915_sw_fence_await_active(&work->chain,
> +						 &vma->active,
> +						 I915_ACTIVE_AWAIT_ACTIVE);
> +		if (err)
> +			goto err_unpin;
> +	}
> +
> +	err = set_bind_fence(fence, work);
> +	if (err)
> +		goto err_unpin;
> +
> +	if (set) {
> +		fence->start = vma->node.start;
> +		fence->size  = vma->fence_size;
> +		fence->stride = i915_gem_object_get_stride(vma->obj);
> +		fence->tiling = i915_gem_object_get_tiling(vma->obj);
> +
> +		vma->fence = fence;
> +	} else {
> +		fence->tiling = 0;
> +		vma->fence = NULL;
> +	}
> +
> +	set = xchg(&fence->vma, set);
> +	if (set && set != vma) {
> +		GEM_BUG_ON(set->fence != fence);
> +		WRITE_ONCE(set->fence, NULL);
> +		i915_vma_revoke_mmap(set);
> +	}
> +
> +	return 0;
> +
> +err_unpin:
> +	atomic_dec(&fence->pin_count);
> +	return err;
> +}
> +
> +void __i915_vma_apply_fence_async(struct i915_vma *vma)
> +{
> +	struct i915_fence_reg *fence = vma->fence;
> +
> +	if (fence->dirty)
> +		fence_write(fence);

What is async in here?

> +}
> +
>   /**
>    * i915_vma_pin_fence - set up fencing for a vma
>    * @vma: vma to map through a fence reg
> diff --git a/drivers/gpu/drm/i915/gt/intel_ggtt_fencing.h b/drivers/gpu/drm/i915/gt/intel_ggtt_fencing.h
> index 9eef679e1311..d306ac14d47e 100644
> --- a/drivers/gpu/drm/i915/gt/intel_ggtt_fencing.h
> +++ b/drivers/gpu/drm/i915/gt/intel_ggtt_fencing.h
> @@ -30,6 +30,7 @@
>   
>   #include "i915_active.h"
>   
> +struct dma_fence_work;
>   struct drm_i915_gem_object;
>   struct i915_ggtt;
>   struct i915_vma;
> @@ -70,6 +71,10 @@ void i915_gem_object_do_bit_17_swizzle(struct drm_i915_gem_object *obj,
>   void i915_gem_object_save_bit_17_swizzle(struct drm_i915_gem_object *obj,
>   					 struct sg_table *pages);
>   
> +int __i915_vma_pin_fence_async(struct i915_vma *vma,
> +			       struct dma_fence_work *work);
> +void __i915_vma_apply_fence_async(struct i915_vma *vma);
> +
>   void intel_ggtt_init_fences(struct i915_ggtt *ggtt);
>   void intel_ggtt_fini_fences(struct i915_ggtt *ggtt);
>   
> 

Regards,

Tvrtko
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Intel-gfx] [PATCH 14/20] drm/i915/gem: Include cmdparser in common execbuf pinning
  2020-07-06  6:19 ` [Intel-gfx] [PATCH 14/20] drm/i915/gem: Include cmdparser in common execbuf pinning Chris Wilson
@ 2020-07-14 12:48   ` Tvrtko Ursulin
  0 siblings, 0 replies; 65+ messages in thread
From: Tvrtko Ursulin @ 2020-07-14 12:48 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 06/07/2020 07:19, Chris Wilson wrote:
> Pull the cmdparser allocations in to the reservation phase, and then
> they are included in the common vma pinning pass.
> 
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> ---
>   .../gpu/drm/i915/gem/i915_gem_execbuffer.c    | 348 ++++++++++--------
>   drivers/gpu/drm/i915/gem/i915_gem_object.h    |  10 +
>   drivers/gpu/drm/i915/i915_cmd_parser.c        |  21 +-
>   3 files changed, 218 insertions(+), 161 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> index c14c3b7e0dfd..8e4681427ce3 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> @@ -25,6 +25,7 @@
>   #include "i915_gem_clflush.h"
>   #include "i915_gem_context.h"
>   #include "i915_gem_ioctls.h"
> +#include "i915_memcpy.h"
>   #include "i915_sw_fence_work.h"
>   #include "i915_trace.h"
>   
> @@ -52,6 +53,7 @@ struct eb_bind_vma {
>   
>   struct eb_vma_array {
>   	struct kref kref;
> +	struct list_head aux_list;

Why is the aux_list needed (code comment would do).

>   	struct eb_vma vma[];
>   };
>   
> @@ -246,7 +248,6 @@ struct i915_execbuffer {
>   
>   	struct i915_request *request; /** our request to build */
>   	struct eb_vma *batch; /** identity of the batch obj/vma */
> -	struct i915_vma *trampoline; /** trampoline used for chaining */
>   
>   	/** actual size of execobj[] as we may extend it for the cmdparser */
>   	unsigned int buffer_count;
> @@ -281,6 +282,11 @@ struct i915_execbuffer {
>   		unsigned int rq_size;
>   	} reloc_cache;
>   
> +	struct eb_cmdparser {
> +		struct eb_vma *shadow;
> +		struct eb_vma *trampoline;
> +	} parser;
> +
>   	u64 invalid_flags; /** Set of execobj.flags that are invalid */
>   	u32 context_flags; /** Set of execobj.flags to insert from the ctx */
>   
> @@ -298,6 +304,10 @@ struct i915_execbuffer {
>   	struct eb_vma_array *array;
>   };
>   
> +static struct drm_i915_gem_exec_object2 no_entry = {
> +	.offset = -1ull

Is the -1 ever used or just the unique element address is enough?

> +};
> +
>   static inline bool eb_use_cmdparser(const struct i915_execbuffer *eb)
>   {
>   	return intel_engine_requires_cmd_parser(eb->engine) ||
> @@ -314,6 +324,7 @@ static struct eb_vma_array *eb_vma_array_create(unsigned int count)
>   		return NULL;
>   
>   	kref_init(&arr->kref);
> +	INIT_LIST_HEAD(&arr->aux_list);
>   	arr->vma[0].vma = NULL;
>   
>   	return arr;
> @@ -339,16 +350,31 @@ static inline void eb_unreserve_vma(struct eb_vma *ev)
>   		       __EXEC_OBJECT_HAS_FENCE);
>   }
>   
> +static void eb_vma_destroy(struct eb_vma *ev)
> +{
> +	eb_unreserve_vma(ev);
> +	i915_vma_put(ev->vma);
> +}
> +
> +static void eb_destroy_aux(struct eb_vma_array *arr)
> +{
> +	struct eb_vma *ev, *en;
> +
> +	list_for_each_entry_safe(ev, en, &arr->aux_list, reloc_link) {
> +		eb_vma_destroy(ev);
> +		kfree(ev);
> +	}
> +}
> +
>   static void eb_vma_array_destroy(struct kref *kref)
>   {
>   	struct eb_vma_array *arr = container_of(kref, typeof(*arr), kref);
> -	struct eb_vma *ev = arr->vma;
> +	struct eb_vma *ev;
>   
> -	while (ev->vma) {
> -		eb_unreserve_vma(ev);
> -		i915_vma_put(ev->vma);
> -		ev++;
> -	}
> +	eb_destroy_aux(arr);
> +
> +	for (ev = arr->vma; ev->vma; ev++)
> +		eb_vma_destroy(ev);
>   
>   	kvfree(arr);
>   }
> @@ -396,8 +422,8 @@ eb_lock_vma(struct i915_execbuffer *eb, struct ww_acquire_ctx *acquire)
>   
>   static int eb_create(struct i915_execbuffer *eb)
>   {
> -	/* Allocate an extra slot for use by the command parser + sentinel */
> -	eb->array = eb_vma_array_create(eb->buffer_count + 2);
> +	/* Allocate an extra slot for use by the sentinel */
> +	eb->array = eb_vma_array_create(eb->buffer_count + 1);
>   	if (!eb->array)
>   		return -ENOMEM;
>   
> @@ -1078,7 +1104,7 @@ static int eb_reserve_vma(struct eb_vm_work *work, struct eb_bind_vma *bind)
>   	GEM_BUG_ON(!(drm_mm_node_allocated(&vma->node) ^
>   		     drm_mm_node_allocated(&bind->hole)));
>   
> -	if (entry->offset != vma->node.start) {
> +	if (entry != &no_entry && entry->offset != vma->node.start) {
>   		entry->offset = vma->node.start | UPDATE;
>   		*work->p_flags |= __EXEC_HAS_RELOC;
>   	}
> @@ -1374,7 +1400,8 @@ static int eb_reserve_vm(struct i915_execbuffer *eb)
>   		struct i915_vma *vma = ev->vma;
>   
>   		if (eb_pin_vma_inplace(eb, entry, ev)) {
> -			if (entry->offset != vma->node.start) {
> +			if (entry != &no_entry &&
> +			    entry->offset != vma->node.start) {
>   				entry->offset = vma->node.start | UPDATE;
>   				eb->args->flags |= __EXEC_HAS_RELOC;
>   			}
> @@ -1514,6 +1541,113 @@ static int eb_reserve_vm(struct i915_execbuffer *eb)
>   	} while (1);
>   }
>   
> +static int eb_alloc_cmdparser(struct i915_execbuffer *eb)
> +{
> +	struct intel_gt_buffer_pool_node *pool;
> +	struct i915_vma *vma;
> +	struct eb_vma *ev;
> +	unsigned int len;
> +	int err;
> +
> +	if (range_overflows_t(u64,
> +			      eb->batch_start_offset, eb->batch_len,
> +			      eb->batch->vma->size)) {
> +		drm_dbg(&eb->i915->drm,
> +			"Attempting to use out-of-bounds batch\n");
> +		return -EINVAL;
> +	}
> +
> +	if (eb->batch_len == 0)
> +		eb->batch_len = eb->batch->vma->size - eb->batch_start_offset;

The two checks kind of don't fit under the "alloc cmdparser" heading. 
Maybe move to separate eb_prepare_batch which then calls 
eb_alloc_cmdparser? I mean it's stupid details so probably not even 
important..

> +
> +	if (!eb_use_cmdparser(eb))
> +		return 0;
> +
> +	len = eb->batch_len;
> +	if (!CMDPARSER_USES_GGTT(eb->i915)) {
> +		/*
> +		 * ppGTT backed shadow buffers must be mapped RO, to prevent
> +		 * post-scan tampering
> +		 */
> +		if (!eb->context->vm->has_read_only) {
> +			drm_dbg(&eb->i915->drm,
> +				"Cannot prevent post-scan tampering without RO capable vm\n");
> +			return -EINVAL;
> +		}
> +	} else {
> +		len += I915_CMD_PARSER_TRAMPOLINE_SIZE;
> +	}
> +
> +	pool = intel_gt_get_buffer_pool(eb->engine->gt, len);
> +	if (IS_ERR(pool))
> +		return PTR_ERR(pool);
> +
> +	ev = kzalloc(sizeof(*ev), GFP_KERNEL);
> +	if (!ev) {
> +		err = -ENOMEM;
> +		goto err_pool;
> +	}
> +
> +	vma = i915_vma_instance(pool->obj, eb->context->vm, NULL);
> +	if (IS_ERR(vma)) {
> +		err = PTR_ERR(vma);
> +		goto err_ev;
> +	}
> +	i915_gem_object_set_readonly(vma->obj);
> +	i915_gem_object_set_cache_coherency(vma->obj, I915_CACHE_LLC);
> +	vma->private = pool;
> +
> +	ev->vma = i915_vma_get(vma);
> +	ev->exec = &no_entry;
> +	list_add(&ev->reloc_link, &eb->array->aux_list);
> +	list_add(&ev->bind_link, &eb->bind_list);
> +	list_add(&ev->submit_link, &eb->submit_list);
> +
> +	if (CMDPARSER_USES_GGTT(eb->i915)) {
> +		eb->parser.trampoline = ev;
> +
> +		/*
> +		 * Special care when binding will be required for full-ppgtt
> +		 * as there will be distinct vm involved, and we will need to
> +		 * separate the binding/eviction passes (different vm->mutex).
> +		 */
> +		if (GEM_WARN_ON(eb->context->vm != &eb->engine->gt->ggtt->vm)) {
> +			ev = kzalloc(sizeof(*ev), GFP_KERNEL);
> +			if (!ev) {
> +				err = -ENOMEM;
> +				goto err_pool;
> +			}
> +
> +			vma = i915_vma_instance(pool->obj,
> +						&eb->engine->gt->ggtt->vm,
> +						NULL);
> +			if (IS_ERR(vma)) {
> +				err = PTR_ERR(vma);
> +				goto err_ev;
> +			}
> +			vma->private = pool;
> +
> +			ev->vma = i915_vma_get(vma);
> +			ev->exec = &no_entry;
> +			list_add(&ev->reloc_link, &eb->array->aux_list);
> +			list_add(&ev->bind_link, &eb->bind_list);
> +			list_add(&ev->submit_link, &eb->submit_list);
> +		}
> +
> +		ev->flags = EXEC_OBJECT_NEEDS_GTT;
> +		eb->batch_flags |= I915_DISPATCH_SECURE;
> +	}
> +
> +	eb->parser.shadow = ev;
> +	return 0;
> +
> +err_ev:
> +	kfree(ev);
> +err_pool:
> +	intel_gt_buffer_pool_put(pool);
> +	return err;
> +}
> +
>   static unsigned int eb_batch_index(const struct i915_execbuffer *eb)
>   {
>   	if (eb->args->flags & I915_EXEC_BATCH_FIRST)
> @@ -1684,9 +1818,7 @@ static void eb_destroy(const struct i915_execbuffer *eb)
>   {
>   	GEM_BUG_ON(eb->reloc_cache.rq);
>   
> -	if (eb->array)
> -		eb_vma_array_put(eb->array);
> -
> +	eb_vma_array_put(eb->array);

This change can go to the patch which adds the code.

>   	if (eb->lut_size > 0)
>   		kfree(eb->buckets);
>   }
> @@ -2306,6 +2438,10 @@ static int eb_relocate(struct i915_execbuffer *eb)
>   	if (err)
>   		return err;
>   
> +	err = eb_alloc_cmdparser(eb);
> +	if (err)
> +		return err;
> +
>   	err = eb_reserve_vm(eb);
>   	if (err)
>   		return err;
> @@ -2392,8 +2528,6 @@ static int eb_move_to_gpu(struct i915_execbuffer *eb)
>   	}
>   	ww_acquire_fini(&acquire);
>   
> -	eb_vma_array_put(fetch_and_zero(&eb->array));

How come this is not needed any more? Maybe it was never needed in which 
case the change can get moved to the original patch.

> -
>   	if (unlikely(err))
>   		goto err_skip;
>   
> @@ -2457,25 +2591,6 @@ static int i915_reset_gen7_sol_offsets(struct i915_request *rq)
>   	return 0;
>   }
>   
> -static struct i915_vma *
> -shadow_batch_pin(struct drm_i915_gem_object *obj,
> -		 struct i915_address_space *vm,
> -		 unsigned int flags)
> -{
> -	struct i915_vma *vma;
> -	int err;
> -
> -	vma = i915_vma_instance(obj, vm, NULL);
> -	if (IS_ERR(vma))
> -		return vma;
> -
> -	err = i915_vma_pin(vma, 0, 0, flags);
> -	if (err)
> -		return ERR_PTR(err);
> -
> -	return vma;
> -}
> -
>   struct eb_parse_work {
>   	struct dma_fence_work base;
>   	struct intel_engine_cs *engine;
> @@ -2502,6 +2617,9 @@ static void __eb_parse_release(struct dma_fence_work *work)
>   {
>   	struct eb_parse_work *pw = container_of(work, typeof(*pw), base);
>   
> +	i915_gem_object_unpin_pages(pw->shadow->obj);
> +	i915_gem_object_unpin_pages(pw->batch->obj);
> +
>   	if (pw->trampoline)
>   		i915_active_release(&pw->trampoline->active);
>   	i915_active_release(&pw->shadow->active);
> @@ -2527,35 +2645,48 @@ __parser_mark_active(struct i915_vma *vma,
>   static int
>   parser_mark_active(struct eb_parse_work *pw, struct intel_timeline *tl)
>   {
> -	int err;
> -
> -	err = __parser_mark_active(pw->shadow, tl, &pw->base.dma);
> -	if (err)
> -		return err;
> -
> -	if (pw->trampoline) {
> -		err = __parser_mark_active(pw->trampoline, tl, &pw->base.dma);
> -		if (err)
> -			return err;
> -	}
> +	GEM_BUG_ON(pw->trampoline &&
> +		   pw->trampoline->private != pw->shadow->private);
>   
> -	return 0;
> +	return __parser_mark_active(pw->shadow, tl, &pw->base.dma);

Trampoling is marked as active somewhere else now?

>   }
>   
>   static int eb_parse_pipeline(struct i915_execbuffer *eb,
>   			     struct i915_vma *shadow,
>   			     struct i915_vma *trampoline)
>   {
> +	struct i915_vma *batch = eb->batch->vma;
>   	struct eb_parse_work *pw;
> +	void *ptr;
>   	int err;
>   
> +	GEM_BUG_ON(!i915_vma_is_pinned(shadow));
> +	GEM_BUG_ON(trampoline && !i915_vma_is_pinned(trampoline));
> +
>   	pw = kzalloc(sizeof(*pw), GFP_KERNEL);
>   	if (!pw)
>   		return -ENOMEM;
>   
> +	ptr = i915_gem_object_pin_map(shadow->obj, I915_MAP_FORCE_WB);

I did not spot any new unpin_map calls for these two (^^^ vvv) so maybe 
they are missing.

> +	if (IS_ERR(ptr)) {
> +		err = PTR_ERR(ptr);
> +		goto err_free;
> +	}
> +
> +	if (!(batch->obj->cache_coherent & I915_BO_CACHE_COHERENT_FOR_READ) &&
> +	    i915_has_memcpy_from_wc()) {
> +		ptr = i915_gem_object_pin_map(batch->obj, I915_MAP_WC);
> +		if (IS_ERR(ptr)) {
> +			err = PTR_ERR(ptr);
> +			goto err_dst;
> +		}
> +	} else {
> +		__i915_gem_object_pin_pages(batch->obj);
> +	}
> +
>   	err = i915_active_acquire(&eb->batch->vma->active);
>   	if (err)
> -		goto err_free;
> +		goto err_src;
>   
>   	err = i915_active_acquire(&shadow->active);
>   	if (err)
> @@ -2620,6 +2751,10 @@ static int eb_parse_pipeline(struct i915_execbuffer *eb,
>   	i915_active_release(&shadow->active);
>   err_batch:
>   	i915_active_release(&eb->batch->vma->active);
> +err_src:
> +	i915_gem_object_unpin_pages(batch->obj);
> +err_dst:
> +	i915_gem_object_unpin_pages(shadow->obj);
>   err_free:
>   	kfree(pw);
>   	return err;
> @@ -2627,82 +2762,26 @@ static int eb_parse_pipeline(struct i915_execbuffer *eb,
>   
>   static int eb_parse(struct i915_execbuffer *eb)
>   {
> -	struct drm_i915_private *i915 = eb->i915;
> -	struct intel_gt_buffer_pool_node *pool;
> -	struct i915_vma *shadow, *trampoline;
> -	unsigned int len;
>   	int err;
>   
> -	if (!eb_use_cmdparser(eb))
> -		return 0;
> -
> -	len = eb->batch_len;
> -	if (!CMDPARSER_USES_GGTT(eb->i915)) {
> -		/*
> -		 * ppGTT backed shadow buffers must be mapped RO, to prevent
> -		 * post-scan tampering
> -		 */
> -		if (!eb->context->vm->has_read_only) {
> -			drm_dbg(&i915->drm,
> -				"Cannot prevent post-scan tampering without RO capable vm\n");
> -			return -EINVAL;
> -		}
> -	} else {
> -		len += I915_CMD_PARSER_TRAMPOLINE_SIZE;
> -	}
> -
> -	pool = intel_gt_get_buffer_pool(eb->engine->gt, len);
> -	if (IS_ERR(pool))
> -		return PTR_ERR(pool);
> -
> -	shadow = shadow_batch_pin(pool->obj, eb->context->vm, PIN_USER);
> -	if (IS_ERR(shadow)) {
> -		err = PTR_ERR(shadow);
> -		goto err;
> +	if (unlikely(eb->batch->flags & EXEC_OBJECT_WRITE)) {
> +		drm_dbg(&eb->i915->drm,
> +			"Attempting to use self-modifying batch buffer\n");
> +		return -EINVAL;
>   	}
> -	i915_gem_object_set_readonly(shadow->obj);
> -	shadow->private = pool;
> -
> -	trampoline = NULL;
> -	if (CMDPARSER_USES_GGTT(eb->i915)) {
> -		trampoline = shadow;
> -
> -		shadow = shadow_batch_pin(pool->obj,
> -					  &eb->engine->gt->ggtt->vm,
> -					  PIN_GLOBAL);
> -		if (IS_ERR(shadow)) {
> -			err = PTR_ERR(shadow);
> -			shadow = trampoline;
> -			goto err_shadow;
> -		}
> -		shadow->private = pool;
>   
> -		eb->batch_flags |= I915_DISPATCH_SECURE;
> -	}
> +	if (!eb->parser.shadow)
> +		return 0;
>   
> -	err = eb_parse_pipeline(eb, shadow, trampoline);
> +	err = eb_parse_pipeline(eb,
> +				eb->parser.shadow->vma,
> +				eb->parser.trampoline ? eb->parser.trampoline->vma : NULL);
>   	if (err)
> -		goto err_trampoline;
> -
> -	eb->batch = &eb->vma[eb->buffer_count++];
> -	eb->batch->vma = i915_vma_get(shadow);
> -	eb->batch->flags = __EXEC_OBJECT_HAS_PIN;
> -	list_add_tail(&eb->batch->submit_link, &eb->submit_list);
> -	eb->vma[eb->buffer_count].vma = NULL;
> +		return err;
>   
> -	eb->trampoline = trampoline;
> +	eb->batch = eb->parser.shadow;
>   	eb->batch_start_offset = 0;
> -
>   	return 0;
> -
> -err_trampoline:
> -	if (trampoline)
> -		i915_vma_unpin(trampoline);
> -err_shadow:
> -	i915_vma_unpin(shadow);
> -err:
> -	intel_gt_buffer_pool_put(pool);
> -	return err;
>   }
>   
>   static void
> @@ -2751,10 +2830,10 @@ static int eb_submit(struct i915_execbuffer *eb, struct i915_vma *batch)
>   	if (err)
>   		return err;
>   
> -	if (eb->trampoline) {
> +	if (eb->parser.trampoline) {
>   		GEM_BUG_ON(eb->batch_start_offset);
>   		err = eb->engine->emit_bb_start(eb->request,
> -						eb->trampoline->node.start +
> +						eb->parser.trampoline->vma->node.start +
>   						eb->batch_len,
>   						0, 0);
>   		if (err)
> @@ -3239,7 +3318,7 @@ i915_gem_do_execbuffer(struct drm_device *dev,
>   	eb.buffer_count = args->buffer_count;
>   	eb.batch_start_offset = args->batch_start_offset;
>   	eb.batch_len = args->batch_len;
> -	eb.trampoline = NULL;
> +	memset(&eb.parser, 0, sizeof(eb.parser));
>   
>   	eb.batch_flags = 0;
>   	if (args->flags & I915_EXEC_SECURE) {
> @@ -3305,24 +3384,6 @@ i915_gem_do_execbuffer(struct drm_device *dev,
>   		goto err_vma;
>   	}
>   
> -	if (unlikely(eb.batch->flags & EXEC_OBJECT_WRITE)) {
> -		drm_dbg(&i915->drm,
> -			"Attempting to use self-modifying batch buffer\n");
> -		err = -EINVAL;
> -		goto err_vma;
> -	}
> -
> -	if (range_overflows_t(u64,
> -			      eb.batch_start_offset, eb.batch_len,
> -			      eb.batch->vma->size)) {
> -		drm_dbg(&i915->drm, "Attempting to use out-of-bounds batch\n");
> -		err = -EINVAL;
> -		goto err_vma;
> -	}
> -
> -	if (eb.batch_len == 0)
> -		eb.batch_len = eb.batch->vma->size - eb.batch_start_offset;
> -
>   	err = eb_parse(&eb);
>   	if (err)
>   		goto err_vma;
> @@ -3348,7 +3409,7 @@ i915_gem_do_execbuffer(struct drm_device *dev,
>   		vma = i915_gem_object_ggtt_pin(batch->obj, NULL, 0, 0, 0);
>   		if (IS_ERR(vma)) {
>   			err = PTR_ERR(vma);
> -			goto err_parse;
> +			goto err_vma;
>   		}
>   
>   		GEM_BUG_ON(vma->obj != batch->obj);
> @@ -3400,8 +3461,9 @@ i915_gem_do_execbuffer(struct drm_device *dev,
>   	 * to explicitly hold another reference here.
>   	 */
>   	eb.request->batch = batch;
> -	if (batch->private)
> -		intel_gt_buffer_pool_mark_active(batch->private, eb.request);
> +	if (eb.parser.shadow)
> +		intel_gt_buffer_pool_mark_active(eb.parser.shadow->vma->private,
> +						 eb.request);
>   
>   	trace_i915_request_queue(eb.request, eb.batch_flags);
>   	err = eb_submit(&eb, batch);
> @@ -3428,13 +3490,9 @@ i915_gem_do_execbuffer(struct drm_device *dev,
>   err_batch_unpin:
>   	if (eb.batch_flags & I915_DISPATCH_SECURE)
>   		i915_vma_unpin(batch);
> -err_parse:
> -	if (batch->private)
> -		intel_gt_buffer_pool_put(batch->private);
> -	i915_vma_put(batch);
>   err_vma:
> -	if (eb.trampoline)
> -		i915_vma_unpin(eb.trampoline);
> +	if (eb.parser.shadow)
> +		intel_gt_buffer_pool_put(eb.parser.shadow->vma->private);
>   	eb_unpin_engine(&eb);
>   err_context:
>   	i915_gem_context_put(eb.gem_context);
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.h b/drivers/gpu/drm/i915/gem/i915_gem_object.h
> index 2faa481cc18f..25714bf70b6a 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_object.h
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_object.h
> @@ -372,6 +372,16 @@ enum i915_map_type {
>   void *__must_check i915_gem_object_pin_map(struct drm_i915_gem_object *obj,
>   					   enum i915_map_type type);
>   
> +static inline void *__i915_gem_object_mapping(struct drm_i915_gem_object *obj)
> +{
> +	return page_mask_bits(obj->mm.mapping);
> +}
> +
> +static inline int __i915_gem_object_mapping_type(struct drm_i915_gem_object *obj)
> +{
> +	return page_unmask_bits(obj->mm.mapping);
> +}
> +
>   void __i915_gem_object_flush_map(struct drm_i915_gem_object *obj,
>   				 unsigned long offset,
>   				 unsigned long size);
> diff --git a/drivers/gpu/drm/i915/i915_cmd_parser.c b/drivers/gpu/drm/i915/i915_cmd_parser.c
> index 372354d33f55..dc8770206bb8 100644
> --- a/drivers/gpu/drm/i915/i915_cmd_parser.c
> +++ b/drivers/gpu/drm/i915/i915_cmd_parser.c
> @@ -1140,29 +1140,22 @@ static u32 *copy_batch(struct drm_i915_gem_object *dst_obj,
>   {
>   	bool needs_clflush;
>   	void *dst, *src;
> -	int ret;
>   
> -	dst = i915_gem_object_pin_map(dst_obj, I915_MAP_FORCE_WB);
> -	if (IS_ERR(dst))
> -		return dst;
> +	GEM_BUG_ON(!i915_gem_object_has_pages(src_obj));
>   
> -	ret = i915_gem_object_pin_pages(src_obj);
> -	if (ret) {
> -		i915_gem_object_unpin_map(dst_obj);
> -		return ERR_PTR(ret);
> -	}
> +	dst = __i915_gem_object_mapping(dst_obj);
> +	GEM_BUG_ON(!dst);
>   
>   	needs_clflush =
>   		!(src_obj->cache_coherent & I915_BO_CACHE_COHERENT_FOR_READ);
>   
>   	src = ERR_PTR(-ENODEV);
>   	if (needs_clflush && i915_has_memcpy_from_wc()) {
> -		src = i915_gem_object_pin_map(src_obj, I915_MAP_WC);
> -		if (!IS_ERR(src)) {
> +		if (__i915_gem_object_mapping_type(src_obj) == I915_MAP_WC) {
> +			src = __i915_gem_object_mapping(src_obj);
>   			i915_unaligned_memcpy_from_wc(dst,
>   						      src + offset,
>   						      length);
> -			i915_gem_object_unpin_map(src_obj);
>   		}
>   	}
>   	if (IS_ERR(src)) {
> @@ -1198,9 +1191,6 @@ static u32 *copy_batch(struct drm_i915_gem_object *dst_obj,
>   		}
>   	}
>   
> -	i915_gem_object_unpin_pages(src_obj);
> -
> -	/* dst_obj is returned with vmap pinned */
>   	return dst;
>   }
>   
> @@ -1546,7 +1536,6 @@ int intel_engine_cmd_parser(struct intel_engine_cs *engine,
>   
>   	if (!IS_ERR_OR_NULL(jump_whitelist))
>   		kfree(jump_whitelist);
> -	i915_gem_object_unpin_map(shadow->obj);
>   	return ret;
>   }
>   
> 

Regards,

Tvrtko
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Intel-gfx] [PATCH 09/20] drm/i915/gem: Assign context id for async work
  2020-07-13 12:22                   ` Tvrtko Ursulin
@ 2020-07-14 14:01                     ` Chris Wilson
  0 siblings, 0 replies; 65+ messages in thread
From: Chris Wilson @ 2020-07-14 14:01 UTC (permalink / raw)
  To: Tvrtko Ursulin, intel-gfx

Quoting Tvrtko Ursulin (2020-07-13 13:22:19)
> 
> On 09/07/2020 13:07, Chris Wilson wrote:
> > Quoting Tvrtko Ursulin (2020-07-09 12:59:51)
> >>
> >> On 09/07/2020 12:07, Chris Wilson wrote:
> >>> Quoting Tvrtko Ursulin (2020-07-09 12:01:29)
> >>>>
> >>>> On 08/07/2020 16:36, Chris Wilson wrote:
> >>>>> Quoting Tvrtko Ursulin (2020-07-08 15:24:20)
> >>>>>> And what is the effective behaviour you get with N contexts - emit N
> >>>>>> concurrent operations and for N + 1 block in execbuf?
> >>>>>
> >>>>> Each context defines a timeline. A task is not ready to run until the
> >>>>> task before it in its timeline is completed. So we don't block in
> >>>>> execbuf, the scheduler waits until the request is ready before putting
> >>>>> it into the HW queues -- i.e. the number chain of fences with everything
> >>>>> that entails about ensuring it runs to completion [whether successfully
> >>>>> or not, if not we then rely on the error propagation to limit the damage
> >>>>> and report it back to the user if they kept a fence around to inspect].
> >>>>
> >>>> Okay but what is the benefit of N contexts in this series, before the
> >>>> work is actually spread over ctx async width CPUs? Is there any? If not
> >>>> I would prefer this patch is delayed until the time some actual
> >>>> parallelism is ready to be added.
> >>>
> >>> We currently submit an unbounded amount of work. This patch is added
> >>> along with its user to restrict the amount of work allowed to run in
> >>> parallel, and also is used to [crudely] serialise the multiple threads
> >>> attempting to allocate space in the vm when we completely exhaust that
> >>> address space. We need at least one fence-context id for each user, this
> >>> took the opportunity to generalise that to N ids for each user.
> >>
> >> Right, this is what I asked at the beginning - restricting amount of
> >> work run in parallel - does mean there is some "blocking"/serialisation
> >> during execbuf? Or it is all async but then what is restricted?
> > 
> > It's all* async, so the number of workqueues we utilise is restricted,
> > and so limits the number of CPUs we allow the one context to spread
> > across with multiple execbufs.
> > 
> > *fsvo all.
> 
> Okay.
> 
> Related topic - have we ever thought about what happens when fence 
> context id wraps? I know it's 64-bit, and even with this patch giving 
> out num_cpus blocks, it still feels impossible that it would wrap in 
> normal use. But I wonder if malicious client could create/destroy 
> contexts to cause a wrap and then how well we handle it. I am probably 
> just underestimating today how big 64-bit is and how many ioctls that 
> would require..

I've had cold sweats. We will get silent glitches. I *don't* think we
will corrupt kernel data and oops, but we will corrupt user data.
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Intel-gfx] [PATCH 11/20] drm/i915/gem: Separate the ww_mutex walker into its own list
  2020-07-13 14:53   ` Tvrtko Ursulin
@ 2020-07-14 14:10     ` Chris Wilson
  0 siblings, 0 replies; 65+ messages in thread
From: Chris Wilson @ 2020-07-14 14:10 UTC (permalink / raw)
  To: Tvrtko Ursulin, intel-gfx

Quoting Tvrtko Ursulin (2020-07-13 15:53:56)
> 
> On 06/07/2020 07:19, Chris Wilson wrote:
> Just a temporary stage... are we reviewing those? Best if they can be 
> avoided.

Yes, I am not chuffed in having it. But with the transition from using
an array of execobj[] to having a list that includes the supplementary
objects, the inplace swap() now breaks the lists. We would have to do a
bunch of list_replace() to preserve them. At the moment, I think this is
the lessor of evils, although it is quite hideous.
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Intel-gfx] [PATCH 12/20] drm/i915/gem: Asynchronous GTT unbinding
  2020-07-14  9:02   ` Tvrtko Ursulin
@ 2020-07-14 15:05     ` Chris Wilson
  0 siblings, 0 replies; 65+ messages in thread
From: Chris Wilson @ 2020-07-14 15:05 UTC (permalink / raw)
  To: Tvrtko Ursulin, intel-gfx; +Cc: Matthew Auld

Quoting Tvrtko Ursulin (2020-07-14 10:02:35)
> 
> On 06/07/2020 07:19, Chris Wilson wrote:
> > It is reasonably common for userspace (even modern drivers like iris) to
> > reuse an active address for a new buffer. This would cause the
> > application to stall under its mutex (originally struct_mutex) until the
> > old batches were idle and it could synchronously remove the stale PTE.
> > However, we can queue up a job that waits on the signal for the old
> > nodes to complete and upon those signals, remove the old nodes replacing
> > them with the new ones for the batch. This is still CPU driven, but in
> > theory we can do the GTT patching from the GPU. The job itself has a
> > completion signal allowing the execbuf to wait upon the rebinding, and
> > also other observers to coordinate with the common VM activity.
> > 
> > Letting userspace queue up more work, lets it do more stuff without
> > blocking other clients. In turn, we take care not to let it too much
> > concurrent work, creating a small number of queues for each context to
> > limit the number of concurrent tasks.
> 
> This is a monster patch.. what is the end result here? If there are a 
> few conflicts they can go async, but if more than "concurrency width" 
> need evict then it will be synchronous?

No, if userspace gets far ahead of the binder, the tasks join the queue,
and userspace continues on to complete the submission [without blocking,
for as much as is possible, the waits I have not yet killed should only
be hit if userspace exhausts the entire GTT for itself]. The throttling
for userspace is indeeded to be on acquiring the timeline->mutex and
checking for sufficient ring space, so right at the very start where we
have the O_NONBLOCK check.

> Could you do without this patch for the first implementation? Or come up 
> with ideas to split it up and so make understanding and review manageable?

Challenging. The task is to hold vm->mutex for all the i915_vma_pin(),
which entails breaking i915_vma_pin() into its steps [those before /
during / after vm->mutex]

The intention is really to use the lessons learnt here to generalise it
again.

> > +static int best_hole(struct drm_mm *mm, struct drm_mm_node *node,
> > +                  u64 start, u64 end, u64 align)
> > +{
> > +     struct drm_mm_node *hole;
> > +     u64 size = node->size;
> > +
> > +     do {
> > +             hole = __best_hole(mm, size);
> > +             if (!hole)
> > +                     return -ENOSPC;
> > +
> > +             node->start = round_up(max(start, drm_mm_hole_node_start(hole)),
> > +                                    align);
> > +             if (min(drm_mm_hole_node_end(hole), end) >=
> > +                 node->start + node->size)
> > +                     return drm_mm_reserve_node(mm, node);
> > +
> > +             /*
> > +              * Too expensive to search for every single hole every time,
> > +              * so just look for the next bigger hole, introducing enough
> > +              * space for alignments. Finding the smallest hole with ideal
> > +              * alignment scales very poorly, so we choose to waste space
> > +              * if an alignment is forced. On the other hand, simply
> > +              * randomly selecting an offset in 48b space will cause us
> > +              * to use the majority of that space and exhaust all memory
> > +              * in storing the page directories. Compromise is required.
> > +              */
> > +             size = hole->hole_size + align;
> > +     } while (1);
> > +}
> 
> evict_for_* and all above, feels like it is too much for 
> i915_gem_execbuffer.c. How about that goes to i915_gem_evict.c? Apart 
> from it depending on eb_vm_work..

Right. It's also a question of whether this should work as a part of the
vm (gt/intel_gtt*) or as part of the user rules above that (gem/foo*)
At the moment, execbuf should be the *only* place that cares about
multiple PIN_USER and the risk of eviction interlocks.

> Best hole at least operates solely on drm_mm so should go out, 
> presumably in preparation for moving into drm core.

Yikes. The algorithm is very wasteful and doesn't do a good job of
picking a good candidate, I wouldn't suggest this is suitable for wide
use. It's just less bad than randomly picking (and so spreading evenly)
over 48b. There was an attempt to do a fast drm_mm alignment search, but
has not yet yielded something we can use. So for the time being a hack
to avoid the O(N^2).

> Hm, i915_gem_eb_vm.c for most of the rest?

It's currently 25% of i915_gem_execbuffer.c, ~1000 / 3750 lines, for the
entire chunk of eb_reserve_vm. About 500 for the search/evict/bind code
itself. A sizeable chunk, just not sure where it really belongs. I do
agree that it doesn't really belong here, in one huge file.

> > +static void eb_vma_work_release(struct dma_fence_work *base)
> > +{
> > +     struct eb_vm_work *work = container_of(base, typeof(*work), base);
> > +
> > +     __eb_bind_vma(work);
> 
> Looks like on some early cancels this can try to cleanup things which 
> haven't been set up yet, like the dereference of work->bind[n]. I don't 
> see any protection against that.

work->count = 0, then work->count = as_far_as_is_valid.
 
> Maybe it would be enough if work->count was set after the work has been 
> set up.

Right :-p

> Why is a callback called eb_vma_work_release doing doing the binds 
> anyway? I'd expect release to clean up, not do the work.

Because we have committed the changes in place to i915_vma and have no
rollback. So if we cancel in the middle of those, the work so far must
be committed.

It's because

static void fence_work(struct work_struct *work)
{
        struct dma_fence_work *f = container_of(work, typeof(*f), work);

        if (!f->dma.error) {
                int err;

                err = f->ops->work(f);
                if (err)
                        dma_fence_set_error(&f->dma, err);
        }

        fence_complete(f);
        dma_fence_put(&f->dma);
}

so if we always call f->ops->work(), then the eb release can be sane,
but I need to check the other users. I haven't made my mind up whether
calling f->ops->work() when already in error is a good convention or not.

> > +static int wait_for_unbinds(struct i915_execbuffer *eb,
> > +                         struct list_head *unbound,
> > +                         int pass)
> > +{
> > +     struct eb_vma *ev;
> > +     int err;
> > +
> > +     list_for_each_entry(ev, unbound, unbound_link) {
> > +             struct i915_vma *vma = ev->vma;
> > +
> > +             GEM_BUG_ON(ev->flags & __EXEC_OBJECT_HAS_PIN);
> > +
> > +             if (drm_mm_node_allocated(&vma->node) &&
> > +                 eb_vma_misplaced(ev->exec, vma, ev->flags)) {
> > +                     err = i915_vma_unbind(vma);
> > +                     if (err)
> > +                             return err;
> > +             }
> > +
> > +             /* Wait for previous to avoid reusing vma->node */
> > +             err = i915_vma_wait_for_unbind(vma);
> 
> This waits on vma->active, presumably the current execbuf hasn't added 
> anything to it so far..

This is after the current execbuf has released everything it added, so
the wait here will either be for its own incomplete error work, or for
someone else.

> 
> > +             if (err)
> > +                     return err;
> > +     }
> > +
> > +     switch (pass) {
> > +     default:
> > +             return -ENOSPC;
> > +
> > +     case 2:
> > +             /*
> > +              * Too fragmented, retire everything on the timeline and so
> > +              * make it all [contexts included] available to evict.
> > +              */
> > +             err = wait_for_timeline(eb->context->timeline);
> > +             if (err)
> > +                     return err;
> > +
> > +             fallthrough;
> > +     case 1:
> > +             /* XXX ticket lock */
> > +             if (i915_active_wait(&eb->context->vm->binding))
> 
> .. and this waits on itself? Or?

For contemporary execbuf with inflight binders. If we fall off the
greedy path where we aim to reuse our resident working set and only
that set, we need to coordinate with everyone else also trying to use
the vm so that we allocate the entire thing for ourselves should we need
to. There's a thundering herd problem here in that we may never quiesce
a shared vm to claim it all for us. Same old risks, and we can't do an
extended hold of vm->mutex unless we are able to avoid all allocations
after that point. And that's quite tricky.

> > +             /* No allocations allowed beyond this point */
> > +             if (mutex_lock_interruptible(&vm->mutex))
> > +                     return eb_vm_work_cancel(work, -EINTR);
> > +
> > +             err = eb_vm_throttle(work);
> 
> The effect is installing an async await on the current binding work, 
> with the signaler being what is currently in vm->binding. This could be 
> another execbuf from the same client?

Yes, it's keyed on the GEM context.async_id, so will only be queued
behind execbufs from the same context. Of course, it will accumulate
waits for the actual vma, which may be across clients, as well.
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Intel-gfx] [PATCH 13/20] drm/i915/gem: Bind the fence async for execbuf
  2020-07-14 12:19   ` Tvrtko Ursulin
@ 2020-07-14 15:21     ` Chris Wilson
  0 siblings, 0 replies; 65+ messages in thread
From: Chris Wilson @ 2020-07-14 15:21 UTC (permalink / raw)
  To: Tvrtko Ursulin, intel-gfx

Quoting Tvrtko Ursulin (2020-07-14 13:19:22)
> 
> On 06/07/2020 07:19, Chris Wilson wrote:
> > It is illegal to wait on an another vma while holding the vm->mutex, as
> > that easily leads to ABBA deadlocks (we wait on a second vma that waits
> > on us to release the vm->mutex). So while the vm->mutex exists, move the
> > waiting outside of the lock into the async binding pipeline.
> > 
> > Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> > ---
> >   .../gpu/drm/i915/gem/i915_gem_execbuffer.c    |  21 +--
> >   drivers/gpu/drm/i915/gt/intel_ggtt_fencing.c  | 137 +++++++++++++++++-
> >   drivers/gpu/drm/i915/gt/intel_ggtt_fencing.h  |   5 +
> >   3 files changed, 151 insertions(+), 12 deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> > index 6a406e8798ef..c14c3b7e0dfd 100644
> > --- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> > +++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> > @@ -1056,15 +1056,6 @@ static int eb_reserve_vma(struct eb_vm_work *work, struct eb_bind_vma *bind)
> >               return err;
> >   
> >   pin:
> > -     if (unlikely(exec_flags & EXEC_OBJECT_NEEDS_FENCE)) {
> > -             err = __i915_vma_pin_fence(vma); /* XXX no waiting */
> > -             if (unlikely(err))
> > -                     return err;
> > -
> > -             if (vma->fence)
> > -                     bind->ev->flags |= __EXEC_OBJECT_HAS_FENCE;
> > -     }
> > -
> >       bind_flags &= ~atomic_read(&vma->flags);
> >       if (bind_flags) {
> >               err = set_bind_fence(vma, work);
> > @@ -1095,6 +1086,15 @@ static int eb_reserve_vma(struct eb_vm_work *work, struct eb_bind_vma *bind)
> >       bind->ev->flags |= __EXEC_OBJECT_HAS_PIN;
> >       GEM_BUG_ON(eb_vma_misplaced(entry, vma, bind->ev->flags));
> >   
> > +     if (unlikely(exec_flags & EXEC_OBJECT_NEEDS_FENCE)) {
> > +             err = __i915_vma_pin_fence_async(vma, &work->base);
> > +             if (unlikely(err))
> > +                     return err;
> > +
> > +             if (vma->fence)
> > +                     bind->ev->flags |= __EXEC_OBJECT_HAS_FENCE;
> > +     }
> > +
> >       return 0;
> >   }
> >   
> > @@ -1160,6 +1160,9 @@ static void __eb_bind_vma(struct eb_vm_work *work)
> >               struct eb_bind_vma *bind = &work->bind[n];
> >               struct i915_vma *vma = bind->ev->vma;
> >   
> > +             if (bind->ev->flags & __EXEC_OBJECT_HAS_FENCE)
> > +                     __i915_vma_apply_fence_async(vma);
> > +
> >               if (!bind->bind_flags)
> >                       goto put;
> >   
> > diff --git a/drivers/gpu/drm/i915/gt/intel_ggtt_fencing.c b/drivers/gpu/drm/i915/gt/intel_ggtt_fencing.c
> > index 7fb36b12fe7a..734b6aa61809 100644
> > --- a/drivers/gpu/drm/i915/gt/intel_ggtt_fencing.c
> > +++ b/drivers/gpu/drm/i915/gt/intel_ggtt_fencing.c
> > @@ -21,10 +21,13 @@
> >    * IN THE SOFTWARE.
> >    */
> >   
> > +#include "i915_active.h"
> >   #include "i915_drv.h"
> >   #include "i915_scatterlist.h"
> > +#include "i915_sw_fence_work.h"
> >   #include "i915_pvinfo.h"
> >   #include "i915_vgpu.h"
> > +#include "i915_vma.h"
> >   
> >   /**
> >    * DOC: fence register handling
> > @@ -340,19 +343,37 @@ static struct i915_fence_reg *fence_find(struct i915_ggtt *ggtt)
> >       return ERR_PTR(-EDEADLK);
> >   }
> >   
> > +static int fence_wait_bind(struct i915_fence_reg *reg)
> > +{
> > +     struct dma_fence *fence;
> > +     int err = 0;
> > +
> > +     fence = i915_active_fence_get(&reg->active.excl);
> > +     if (fence) {
> > +             err = dma_fence_wait(fence, true);
> > +             dma_fence_put(fence);
> > +     }
> > +
> > +     return err;
> > +}
> > +
> >   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_fence_reg *fence = vma->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;
> > +     if (fence) {
> >               GEM_BUG_ON(fence->vma != vma);
> > +
> > +             err = fence_wait_bind(fence);
> > +             if (err)
> > +                     return err;
> > +
> >               atomic_inc(&fence->pin_count);
> >               if (!fence->dirty) {
> >                       list_move_tail(&fence->link, &ggtt->fence_list);
> > @@ -384,6 +405,116 @@ int __i915_vma_pin_fence(struct i915_vma *vma)
> >       return err;
> >   }
> >   
> > +static int set_bind_fence(struct i915_fence_reg *fence,
> > +                       struct dma_fence_work *work)
> > +{
> > +     struct dma_fence *prev;
> > +     int err;
> > +
> > +     if (rcu_access_pointer(fence->active.excl.fence) == &work->dma)
> > +             return 0;
> 
> What is this checking for?

Paranoia to avoid waiting upon ourselves. Should be possible to declare
it a GEM_BUG_ON.

> > +     err = i915_sw_fence_await_active(&work->chain,
> > +                                      &fence->active,
> > +                                      I915_ACTIVE_AWAIT_ACTIVE);
> > +     if (err)
> > +             return err;
> > +
> > +     if (i915_active_acquire(&fence->active))
> > +             return -ENOENT;
> > +
> > +     prev = i915_active_set_exclusive(&fence->active, &work->dma);
> > +     if (unlikely(prev)) {
> > +             err = i915_sw_fence_await_dma_fence(&work->chain, prev, 0,
> > +                                                 GFP_NOWAIT | __GFP_NOWARN);
> 
> This is a potential allocation under vm->mutex.

Aye, and it is not allowed to shrink because we hold vm->mutex.
It's the biggest problem with trying to build up a job with many
dependencies, where each vma may need to evict multiple others and have
a number of awaits.

> > +             dma_fence_put(prev);
> > +     }
> > +
> > +     i915_active_release(&fence->active);
> > +     return err < 0 ? err : 0;
> > +}
> > +
> > +int __i915_vma_pin_fence_async(struct i915_vma *vma,
> > +                            struct dma_fence_work *work)
> > +{
> > +     struct i915_ggtt *ggtt = i915_vm_to_ggtt(vma->vm);
> > +     struct i915_vma *set = i915_gem_object_is_tiled(vma->obj) ? vma : NULL;
> > +     struct i915_fence_reg *fence = vma->fence;
> > +     int err;
> > +
> > +     lockdep_assert_held(&vma->vm->mutex);
> > +
> > +     /* Just update our place in the LRU if our fence is getting reused. */
> > +     if (fence) {
> > +             GEM_BUG_ON(fence->vma != vma);
> > +             GEM_BUG_ON(!i915_vma_is_map_and_fenceable(vma));
> > +     } else if (set) {
> > +             if (!i915_vma_is_map_and_fenceable(vma))
> > +                     return -EINVAL;
> > +
> > +             fence = fence_find(ggtt);
> > +             if (IS_ERR(fence))
> > +                     return -ENOSPC;
> > +
> > +             GEM_BUG_ON(atomic_read(&fence->pin_count));
> > +             fence->dirty = true;
> > +     } else {
> > +             return 0;
> > +     }
> > +
> > +     atomic_inc(&fence->pin_count);
> > +     list_move_tail(&fence->link, &ggtt->fence_list);
> > +     if (!fence->dirty)
> > +             return 0;
> > +
> > +     if (INTEL_GEN(fence_to_i915(fence)) < 4 &&
> > +         rcu_access_pointer(vma->active.excl.fence) != &work->dma) {
> 
> This second part is the same check as in set_bind_fence.

The await for implicit unfenced blits? That's subtly different.
 
> Should it go to a helper with a self-descriptive name and then this 
> function would gate both the gen > 4 check and the set_bind_fence call 
> under it?
> 
> > +             /* implicit 'unfenced' GPU blits */
> > +             err = i915_sw_fence_await_active(&work->chain,
> > +                                              &vma->active,
> > +                                              I915_ACTIVE_AWAIT_ACTIVE);
> > +             if (err)
> > +                     goto err_unpin;
> > +     }
> > +
> > +     err = set_bind_fence(fence, work);
> > +     if (err)
> > +             goto err_unpin;
> > +
> > +     if (set) {
> > +             fence->start = vma->node.start;
> > +             fence->size  = vma->fence_size;
> > +             fence->stride = i915_gem_object_get_stride(vma->obj);
> > +             fence->tiling = i915_gem_object_get_tiling(vma->obj);
> > +
> > +             vma->fence = fence;
> > +     } else {
> > +             fence->tiling = 0;
> > +             vma->fence = NULL;
> > +     }
> > +
> > +     set = xchg(&fence->vma, set);
> > +     if (set && set != vma) {
> > +             GEM_BUG_ON(set->fence != fence);
> > +             WRITE_ONCE(set->fence, NULL);
> > +             i915_vma_revoke_mmap(set);
> > +     }
> > +
> > +     return 0;
> > +
> > +err_unpin:
> > +     atomic_dec(&fence->pin_count);
> > +     return err;
> > +}
> > +
> > +void __i915_vma_apply_fence_async(struct i915_vma *vma)
> > +{
> > +     struct i915_fence_reg *fence = vma->fence;
> > +
> > +     if (fence->dirty)
> > +             fence_write(fence);
> 
> What is async in here?

This executes the async work. The first function was pin_fence_async, so
this was apply_fence_async because that looked symmetrical.
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [Intel-gfx] s/obj->mm.lock//
  2020-07-06  6:19 [Intel-gfx] s/obj->mm.lock// Chris Wilson
                   ` (23 preceding siblings ...)
  2020-07-06  7:55 ` [Intel-gfx] ✗ Fi.CI.IGT: failure " Patchwork
@ 2020-07-27 18:53 ` Thomas Hellström (Intel)
  24 siblings, 0 replies; 65+ messages in thread
From: Thomas Hellström (Intel) @ 2020-07-27 18:53 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 7/6/20 8:19 AM, Chris Wilson wrote:
> This is the easy part; pulling reservation of multiple objects under an
> ww acquire context. With one simple rule that eviction is handled by the
> ww acquire context, we can carefully transition the driver over to using
> eviction. Instead of feeding the acquire context everywhere, we make the
> caller gather up all the objects they need to acquire into the context,
> then acquire their backing store. The major boon here is that by
> providing a clean set of objects (that we have not yet started to
> acquire any auxiliary attachments for) to the acquire context, it can
> handle all the EDEADLK processing for us [since it is a pure locking
> operation and does not need to release attachments upon revoking the
> locks].
>
> As a sketch of what that would look like, to illustrate the major work
> remaining:
>
> static int evict(struct drm_i915_gem_object *obj, struct i915_acquire_ctx *ctx)
> {
> 	struct intel_memory_region *mem = obj->mm->region;
> 	struct drm_i915_gem_object *swap; // struct i915_mm_bo *swap
> 	struct i915_request *rq;
> 	int err;
>
> 	/* swap = mem->create_eviction_target(obj); */
> 	swap = i915_gem_object_create_shmem(mem->i915, obj->base.size);
> 	if (IS_ERR(swap))
> 		return PTR_ERR(swap);
>
> 	err = dma_resv_lock_interruptible(swap, &ctx->ctx);
> 	GEM_BUG_ON(err == -EALREADY);
> 	if (err == -EDEADLK)
> 		goto out;
>
> 	/* Obviously swap has to be carefully chosen so that this may succeed */
> 	err = __i915_gem_object_get_pages_locked(swap);
> 	if (err)
> 		goto out_unlock;
>
> 	rq = pinned_evict_copy(ctx, obj, swap);
> 	if (IS_ERR(rq)) {
> 		err = PTR_ERR(rq);
> 		goto out_unlock;
> 	}
>
> 	err = i915_gem_revoke_mm(obj);
> 	if (err)
> 		goto out_request;
>
> 	/* Alternatively you could wait synchronously! */
> 	mem->release_blocks(&obj->mm->blocks, rq);
> 	i915_mm_bo_put(xchg(&obj->mm, i915_mm_bo_get(swap)));
>
> 	dma_resv_add_exclusive_fence(obj->base.resv, &rq->fence);
> out_request:
> 	i915_request_put(rq);
> out_unlock:
> 	dma_resv_unlock(swap);
> out:
> 	i915_gem_object_put(swap);
> 	return err;
> }
>
> static int relock_all(struct i915_acquire_ctx *ctx)
> {
> 	struct i915_acquire_link *lnk, *lock;
> 	int err;
>
> 	for (lnk = ctx->locked; lnk; lnk = lnk->next)
> 		dma_resv_unlock(lnk->obj->base.resv);
>
> 	lock = fetch_and_zero(&ctx->locked);
> 	while ((lnk = lock)) {
> 		struct drm_i915_gem_object *obj;
>
> 		obj = lnk->obj;
> 		lock = lnk->next;
>
> 		if (ctx->locked)
> 			err = dma_resv_lock_interruptible(obj->base.resv,
> 							  &ctx->ctx);
> 		else
> 			err = dma_resv_lock_slow_interruptible(obj->base.resv,
> 							       &ctx->ctx);
> 		GEM_BUG_ON(err == -EALREADY);
> 		if (err == -EDEADLK) {
> 			struct i915_acquire *old;
>
> 			while ((old = ctx->locked)) {
> 				dma_resv_unlock(old->obj->base.resv);
> 				ctx->locked = old->next;
> 				old->next = lock;
> 				lock = old;
> 			}
>
> 			lnk->next = lock;
> 			lock = lnk;
> 			continue;
> 		}
> 		if (err) {
> 			lock = lnk;
> 			break;
> 		}
>
> 		lnk->next = ctx->locked;
> 		ctx->locked = lnk;
> 	}
>
> 	while ((lnk = lock)) {
> 		lock = lnk->next;
> 		i915_gem_object_put(lnk->obj);
> 		i915_acquire_free(lnk);
> 	}
>
> 	return err;
> }
>
> int i915_acquire_mm(struct i915_acquire_ctx *ctx)
> {
> 	struct i915_acquire_link *lnk;
> 	int n, err;
>
> restart:
> 	for (lnk = ctx->locked; lnk; lnk = lnk->next) {
> 		for (n = 0;
> 		     !i915_gem_object_has_pages(lnk->obj);
> 		     n++) {
> 			struct drm_i915_gem_object *evictee = NULL;
>
> 			mem = get_preferred_memregion_for_object(lnk->obj, n);
> 			if (!mem)
> 				return -ENXIO;
>
> 			while (!i915_gem_object_get_pages(lnk->obj)) {
> 				struct i915_acquire_link *this;
>
> 				evictee = mem->get_eviction_candidate(mem,
> 								      evictee);
> 				if (!evictee)
> 					break;
>
> 				err = dma_resv_lock_interruptible(evictee,
> 								  &ctx->ctx);
> 				if (err == -EALREADY)
> 					continue; /* XXX fragmentation? */
> 				if (err == 0)
> 					err = evict(evictee);
> 				dma_resv_unlock(evictee);

There was a discussion on dri-devel not too long ago, where Christian 
mentioned there is a point holding on to the evictee(s) locks until  the 
get_pages() succeeds to avoid racing with threads wanting to move the 
evictee back into vram. Perhaps something worth considering.

/Thomas


_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 65+ messages in thread

end of thread, other threads:[~2020-07-27 18:53 UTC | newest]

Thread overview: 65+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-07-06  6:19 [Intel-gfx] s/obj->mm.lock// Chris Wilson
2020-07-06  6:19 ` [Intel-gfx] [PATCH 01/20] drm/i915: Preallocate stashes for vma page-directories Chris Wilson
2020-07-06 18:15   ` Matthew Auld
2020-07-06 18:20     ` Chris Wilson
2020-07-06  6:19 ` [Intel-gfx] [PATCH 02/20] drm/i915: Switch to object allocations for page directories Chris Wilson
2020-07-06 19:06   ` Matthew Auld
2020-07-06 19:31     ` Chris Wilson
2020-07-06 20:01     ` Chris Wilson
2020-07-06 21:08       ` Chris Wilson
2020-07-06  6:19 ` [Intel-gfx] [PATCH 03/20] drm/i915/gem: Don't drop the timeline lock during execbuf Chris Wilson
2020-07-08 16:54   ` Tvrtko Ursulin
2020-07-08 18:08     ` Chris Wilson
2020-07-09 10:52       ` Tvrtko Ursulin
2020-07-09 10:57         ` Chris Wilson
2020-07-06  6:19 ` [Intel-gfx] [PATCH 04/20] drm/i915/gem: Rename execbuf.bind_link to unbound_link Chris Wilson
2020-07-10 11:26   ` Tvrtko Ursulin
2020-07-06  6:19 ` [Intel-gfx] [PATCH 05/20] drm/i915/gem: Break apart the early i915_vma_pin from execbuf object lookup Chris Wilson
2020-07-10 11:27   ` Tvrtko Ursulin
2020-07-06  6:19 ` [Intel-gfx] [PATCH 06/20] drm/i915/gem: Remove the call for no-evict i915_vma_pin Chris Wilson
2020-07-06  6:19 ` [Intel-gfx] [PATCH 07/20] drm/i915: Add list_for_each_entry_safe_continue_reverse Chris Wilson
2020-07-06  6:19 ` [Intel-gfx] [PATCH 08/20] drm/i915: Always defer fenced work to the worker Chris Wilson
2020-07-08 12:18   ` Tvrtko Ursulin
2020-07-08 12:25     ` Chris Wilson
2020-07-06  6:19 ` [Intel-gfx] [PATCH 09/20] drm/i915/gem: Assign context id for async work Chris Wilson
2020-07-08 12:26   ` Tvrtko Ursulin
2020-07-08 12:42     ` Chris Wilson
2020-07-08 14:24       ` Tvrtko Ursulin
2020-07-08 15:36         ` Chris Wilson
2020-07-09 11:01           ` Tvrtko Ursulin
2020-07-09 11:07             ` Chris Wilson
2020-07-09 11:59               ` Tvrtko Ursulin
2020-07-09 12:07                 ` Chris Wilson
2020-07-13 12:22                   ` Tvrtko Ursulin
2020-07-14 14:01                     ` Chris Wilson
2020-07-08 12:45     ` Tvrtko Ursulin
2020-07-06  6:19 ` [Intel-gfx] [PATCH 10/20] drm/i915: Export a preallocate variant of i915_active_acquire() Chris Wilson
2020-07-09 14:36   ` Maarten Lankhorst
2020-07-10 12:24     ` Tvrtko Ursulin
2020-07-10 12:32       ` Maarten Lankhorst
2020-07-13 14:29   ` Tvrtko Ursulin
2020-07-06  6:19 ` [Intel-gfx] [PATCH 11/20] drm/i915/gem: Separate the ww_mutex walker into its own list Chris Wilson
2020-07-13 14:53   ` Tvrtko Ursulin
2020-07-14 14:10     ` Chris Wilson
2020-07-06  6:19 ` [Intel-gfx] [PATCH 12/20] drm/i915/gem: Asynchronous GTT unbinding Chris Wilson
2020-07-14  9:02   ` Tvrtko Ursulin
2020-07-14 15:05     ` Chris Wilson
2020-07-06  6:19 ` [Intel-gfx] [PATCH 13/20] drm/i915/gem: Bind the fence async for execbuf Chris Wilson
2020-07-14 12:19   ` Tvrtko Ursulin
2020-07-14 15:21     ` Chris Wilson
2020-07-06  6:19 ` [Intel-gfx] [PATCH 14/20] drm/i915/gem: Include cmdparser in common execbuf pinning Chris Wilson
2020-07-14 12:48   ` Tvrtko Ursulin
2020-07-06  6:19 ` [Intel-gfx] [PATCH 15/20] drm/i915/gem: Include secure batch " Chris Wilson
2020-07-06  6:19 ` [Intel-gfx] [PATCH 16/20] drm/i915/gem: Reintroduce multiple passes for reloc processing Chris Wilson
2020-07-09 15:39   ` Tvrtko Ursulin
2020-07-06  6:19 ` [Intel-gfx] [PATCH 17/20] drm/i915: Add an implementation for i915_gem_ww_ctx locking, v2 Chris Wilson
2020-07-06 17:21   ` kernel test robot
2020-07-06  6:19 ` [Intel-gfx] [PATCH 18/20] drm/i915/gem: Pull execbuf dma resv under a single critical section Chris Wilson
2020-07-06  6:19 ` [Intel-gfx] [PATCH 19/20] drm/i915/gem: Replace i915_gem_object.mm.mutex with reservation_ww_class Chris Wilson
2020-07-09 14:06   ` Maarten Lankhorst
2020-07-06  6:19 ` [Intel-gfx] [PATCH 20/20] drm/i915: Track i915_vma with its own reference counter Chris Wilson
2020-07-06  6:28 ` [Intel-gfx] ✗ Fi.CI.CHECKPATCH: warning for series starting with [01/20] drm/i915: Preallocate stashes for vma page-directories Patchwork
2020-07-06  6:29 ` [Intel-gfx] ✗ Fi.CI.SPARSE: " Patchwork
2020-07-06  6:51 ` [Intel-gfx] ✓ Fi.CI.BAT: success " Patchwork
2020-07-06  7:55 ` [Intel-gfx] ✗ Fi.CI.IGT: failure " Patchwork
2020-07-27 18:53 ` [Intel-gfx] s/obj->mm.lock// Thomas Hellström (Intel)

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.