All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 01/21] drm/i915: Restrict the aliasing-ppgtt to the size of the ggtt
@ 2019-09-02  4:02 Chris Wilson
  2019-09-02  4:02 ` [PATCH 02/21] drm/i915: Report aliasing ppgtt size as ggtt size Chris Wilson
                   ` (23 more replies)
  0 siblings, 24 replies; 53+ messages in thread
From: Chris Wilson @ 2019-09-02  4:02 UTC (permalink / raw)
  To: intel-gfx; +Cc: Matthew Auld

The aliasing-ppgtt is not allowed to be smaller than the ggtt, nor
should we advertise it as being any bigger, or else we may get sued for
false advertisement.

Testcase: igt/gem_exec_big
Fixes: 0b718ba1e884 ("drm/i915/gtt: Downgrade Cherryview back to aliasing-ppgtt")
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Matthew Auld <matthew.auld@intel.com>
---
 drivers/gpu/drm/i915/i915_gem_gtt.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
index ee51fd1a6207..906dc6fff383 100644
--- a/drivers/gpu/drm/i915/i915_gem_gtt.c
+++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
@@ -2597,6 +2597,8 @@ 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;
 
+	ppgtt->vm.total = ggtt->vm.total;
+
 	return 0;
 
 err_ppgtt:
-- 
2.23.0

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

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

* [PATCH 02/21] drm/i915: Report aliasing ppgtt size as ggtt size
  2019-09-02  4:02 [PATCH 01/21] drm/i915: Restrict the aliasing-ppgtt to the size of the ggtt Chris Wilson
@ 2019-09-02  4:02 ` Chris Wilson
  2019-09-02  8:55   ` Matthew Auld
  2019-09-02  4:02 ` [PATCH 03/21] drm/i915/execlists: Ignore lost completion events Chris Wilson
                   ` (22 subsequent siblings)
  23 siblings, 1 reply; 53+ messages in thread
From: Chris Wilson @ 2019-09-02  4:02 UTC (permalink / raw)
  To: intel-gfx

The aliasing-ppgtt is constrained to be the same size as the Global GTT
since it aliases the same address space. Simplifying gtt size reporting
in this case.

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

diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context.c b/drivers/gpu/drm/i915/gem/i915_gem_context.c
index b8969605f4e8..f1c0e5d958f3 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_context.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_context.c
@@ -2231,8 +2231,6 @@ int i915_gem_context_getparam_ioctl(struct drm_device *dev, void *data,
 		args->size = 0;
 		if (ctx->vm)
 			args->value = ctx->vm->total;
-		else if (to_i915(dev)->ggtt.alias)
-			args->value = to_i915(dev)->ggtt.alias->vm.total;
 		else
 			args->value = to_i915(dev)->ggtt.vm.total;
 		break;
-- 
2.23.0

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

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

* [PATCH 03/21] drm/i915/execlists: Ignore lost completion events
  2019-09-02  4:02 [PATCH 01/21] drm/i915: Restrict the aliasing-ppgtt to the size of the ggtt Chris Wilson
  2019-09-02  4:02 ` [PATCH 02/21] drm/i915: Report aliasing ppgtt size as ggtt size Chris Wilson
@ 2019-09-02  4:02 ` Chris Wilson
  2019-09-02  4:02 ` [PATCH 04/21] drm/i915: Refresh the errno to vmf_fault translations Chris Wilson
                   ` (21 subsequent siblings)
  23 siblings, 0 replies; 53+ messages in thread
From: Chris Wilson @ 2019-09-02  4:02 UTC (permalink / raw)
  To: intel-gfx

Icelake hit an issue where it missed reporting a completion event and
instead jumped straight to a idle->active event (skipping over the
active->idle and not even hitting the lite-restore preemption).

661497511us : process_csb: rcs0 cs-irq head=11, tail=0
661497512us : process_csb: rcs0 csb[0]: status=0x10008002:0x00000020 [lite-restore]
661497512us : trace_ports: rcs0: preempted { 28cc8:11052, 0:0 }
661497513us : trace_ports: rcs0: promote { 28cc8:11054, 0:0 }
661497514us : __i915_request_submit: rcs0 fence 28cc8:11056, current 11052
661497514us : __execlists_submission_tasklet: rcs0: queue_priority_hint:-2147483648, submit:yes
661497515us : trace_ports: rcs0: submit { 28cc8:11056, 0:0 }
661497530us : process_csb: rcs0 cs-irq head=0, tail=1
661497530us : process_csb: rcs0 csb[1]: status=0x10008002:0x00000020 [lite-restore]
661497531us : trace_ports: rcs0: preempted { 28cc8:11054!, 0:0 }
661497535us : trace_ports: rcs0: promote { 28cc8:11056, 0:0 }
661497540us : __i915_request_submit: rcs0 fence 28cc8:11058, current 11054
661497544us : __execlists_submission_tasklet: rcs0: queue_priority_hint:-2147483648, submit:yes
661497545us : trace_ports: rcs0: submit { 28cc8:11058, 0:0 }
661497553us : process_csb: rcs0 cs-irq head=1, tail=2
661497553us : process_csb: rcs0 csb[2]: status=0x10000001:0x00000000 [idle->active]
661497574us : process_csb: process_csb:1538 GEM_BUG_ON(*execlists->active)

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Mika Kuoppala <mika.kuoppala@linux.intel.com>
---
 drivers/gpu/drm/i915/gt/intel_lrc.c | 63 +++++++++--------------------
 1 file changed, 18 insertions(+), 45 deletions(-)

diff --git a/drivers/gpu/drm/i915/gt/intel_lrc.c b/drivers/gpu/drm/i915/gt/intel_lrc.c
index 87b7473a6dfb..14a4df45e116 100644
--- a/drivers/gpu/drm/i915/gt/intel_lrc.c
+++ b/drivers/gpu/drm/i915/gt/intel_lrc.c
@@ -693,6 +693,9 @@ trace_ports(const struct intel_engine_execlists *execlists,
 	const struct intel_engine_cs *engine =
 		container_of(execlists, typeof(*engine), execlists);
 
+	if (!ports[0])
+		return;
+
 	GEM_TRACE("%s: %s { %llx:%lld%s, %llx:%lld }\n",
 		  engine->name, msg,
 		  ports[0]->fence.context,
@@ -1371,13 +1374,6 @@ reset_in_progress(const struct intel_engine_execlists *execlists)
 	return unlikely(!__tasklet_is_enabled(&execlists->tasklet));
 }
 
-enum csb_step {
-	CSB_NOP,
-	CSB_PROMOTE,
-	CSB_PREEMPT,
-	CSB_COMPLETE,
-};
-
 /*
  * Starting with Gen12, the status has a new format:
  *
@@ -1404,7 +1400,7 @@ enum csb_step {
  *     bits 47-57: sw context id of the lrc the GT switched away from
  *     bits 58-63: sw counter of the lrc the GT switched away from
  */
-static inline enum csb_step
+static inline bool
 gen12_csb_parse(const struct intel_engine_execlists *execlists, const u32 *csb)
 {
 	u32 lower_dw = csb[0];
@@ -1414,7 +1410,7 @@ gen12_csb_parse(const struct intel_engine_execlists *execlists, const u32 *csb)
 	bool new_queue = lower_dw & GEN12_CTX_STATUS_SWITCHED_TO_NEW_QUEUE;
 
 	if (!ctx_away_valid && ctx_to_valid)
-		return CSB_PROMOTE;
+		return true;
 
 	/*
 	 * The context switch detail is not guaranteed to be 5 when a preemption
@@ -1424,7 +1420,7 @@ gen12_csb_parse(const struct intel_engine_execlists *execlists, const u32 *csb)
 	 * would require some extra handling, but we don't support that.
 	 */
 	if (new_queue && ctx_away_valid)
-		return CSB_PREEMPT;
+		return true;
 
 	/*
 	 * switch detail = 5 is covered by the case above and we do not expect a
@@ -1433,29 +1429,13 @@ gen12_csb_parse(const struct intel_engine_execlists *execlists, const u32 *csb)
 	 */
 	GEM_BUG_ON(GEN12_CTX_SWITCH_DETAIL(upper_dw));
 
-	if (*execlists->active) {
-		GEM_BUG_ON(!ctx_away_valid);
-		return CSB_COMPLETE;
-	}
-
-	return CSB_NOP;
+	return false;
 }
 
-static inline enum csb_step
+static inline bool
 gen8_csb_parse(const struct intel_engine_execlists *execlists, const u32 *csb)
 {
-	unsigned int status = *csb;
-
-	if (status & GEN8_CTX_STATUS_IDLE_ACTIVE)
-		return CSB_PROMOTE;
-
-	if (status & GEN8_CTX_STATUS_PREEMPTED)
-		return CSB_PREEMPT;
-
-	if (*execlists->active)
-		return CSB_COMPLETE;
-
-	return CSB_NOP;
+	return *csb & (GEN8_CTX_STATUS_IDLE_ACTIVE | GEN8_CTX_STATUS_PREEMPTED);
 }
 
 static void process_csb(struct intel_engine_cs *engine)
@@ -1494,7 +1474,7 @@ static void process_csb(struct intel_engine_cs *engine)
 	rmb();
 
 	do {
-		enum csb_step csb_step;
+		bool promote;
 
 		if (++head == num_entries)
 			head = 0;
@@ -1522,20 +1502,16 @@ static void process_csb(struct intel_engine_cs *engine)
 			  buf[2 * head + 0], buf[2 * head + 1]);
 
 		if (INTEL_GEN(engine->i915) >= 12)
-			csb_step = gen12_csb_parse(execlists, buf + 2 * head);
+			promote = gen12_csb_parse(execlists, buf + 2 * head);
 		else
-			csb_step = gen8_csb_parse(execlists, buf + 2 * head);
-
-		switch (csb_step) {
-		case CSB_PREEMPT: /* cancel old inflight, prepare for switch */
+			promote = gen8_csb_parse(execlists, buf + 2 * head);
+		if (promote) {
+			/* cancel old inflight, prepare for switch */
 			trace_ports(execlists, "preempted", execlists->active);
-
 			while (*execlists->active)
 				execlists_schedule_out(*execlists->active++);
 
-			/* fallthrough */
-		case CSB_PROMOTE: /* switch pending to inflight */
-			GEM_BUG_ON(*execlists->active);
+			/* switch pending to inflight */
 			GEM_BUG_ON(!assert_pending_valid(execlists, "promote"));
 			execlists->active =
 				memcpy(execlists->inflight,
@@ -1550,9 +1526,10 @@ static void process_csb(struct intel_engine_cs *engine)
 				ring_set_paused(engine, 0);
 
 			WRITE_ONCE(execlists->pending[0], NULL);
-			break;
+		} else {
+			GEM_BUG_ON(!*execlists->active);
 
-		case CSB_COMPLETE: /* port0 completed, advanced to port1 */
+			/* port0 completed, advanced to port1 */
 			trace_ports(execlists, "completed", execlists->active);
 
 			/*
@@ -1567,10 +1544,6 @@ static void process_csb(struct intel_engine_cs *engine)
 
 			GEM_BUG_ON(execlists->active - execlists->inflight >
 				   execlists_num_ports(execlists));
-			break;
-
-		case CSB_NOP:
-			break;
 		}
 	} while (head != tail);
 
-- 
2.23.0

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

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

* [PATCH 04/21] drm/i915: Refresh the errno to vmf_fault translations
  2019-09-02  4:02 [PATCH 01/21] drm/i915: Restrict the aliasing-ppgtt to the size of the ggtt Chris Wilson
  2019-09-02  4:02 ` [PATCH 02/21] drm/i915: Report aliasing ppgtt size as ggtt size Chris Wilson
  2019-09-02  4:02 ` [PATCH 03/21] drm/i915/execlists: Ignore lost completion events Chris Wilson
@ 2019-09-02  4:02 ` Chris Wilson
  2019-09-03 15:34   ` Abdiel Janulgue
  2019-09-02  4:02 ` [PATCH 05/21] drm/i915: Replace obj->pin_global with obj->frontbuffer Chris Wilson
                   ` (20 subsequent siblings)
  23 siblings, 1 reply; 53+ messages in thread
From: Chris Wilson @ 2019-09-02  4:02 UTC (permalink / raw)
  To: intel-gfx

It's been a long time since we accidentally reported -EIO upon wedging,
it can now only be generated by failure to swap in a page.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Abdiel Janulgue <abdiel.janulgue@linux.intel.com>
---
 drivers/gpu/drm/i915/gem/i915_gem_mman.c | 39 +++++++++---------------
 1 file changed, 15 insertions(+), 24 deletions(-)

diff --git a/drivers/gpu/drm/i915/gem/i915_gem_mman.c b/drivers/gpu/drm/i915/gem/i915_gem_mman.c
index 261c9bd83f51..82db2b783123 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_mman.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_mman.c
@@ -287,6 +287,9 @@ vm_fault_t i915_gem_fault(struct vm_fault *vmf)
 			view.type = I915_GGTT_VIEW_PARTIAL;
 			vma = i915_gem_object_ggtt_pin(obj, &view, 0, 0, flags);
 		}
+
+		/* The entire mappable GGTT is pinned? Unexpected! */
+		GEM_BUG_ON(vma == ERR_PTR(-ENOSPC));
 	}
 	if (IS_ERR(vma)) {
 		ret = PTR_ERR(vma);
@@ -333,23 +336,19 @@ vm_fault_t i915_gem_fault(struct vm_fault *vmf)
 	i915_gem_object_unpin_pages(obj);
 err:
 	switch (ret) {
-	case -EIO:
-		/*
-		 * We eat errors when the gpu is terminally wedged to avoid
-		 * userspace unduly crashing (gl has no provisions for mmaps to
-		 * fail). But any other -EIO isn't ours (e.g. swap in failure)
-		 * and so needs to be reported.
-		 */
-		if (!intel_gt_is_wedged(ggtt->vm.gt))
-			return VM_FAULT_SIGBUS;
-		/* else, fall through */
-	case -EAGAIN:
-		/*
-		 * EAGAIN means the gpu is hung and we'll wait for the error
-		 * handler to reset everything when re-faulting in
-		 * i915_mutex_lock_interruptible.
-		 */
+	default:
+		WARN_ONCE(ret, "unhandled error in %s: %i\n", __func__, ret);
+		/* fallthrough */
+	case -EIO: /* shmemfs failure from swap device */
+	case -EFAULT: /* purged object */
+		return VM_FAULT_SIGBUS;
+
+	case -ENOSPC: /* shmemfs allocation failure */
+	case -ENOMEM: /* our allocation failure */
+		return VM_FAULT_OOM;
+
 	case 0:
+	case -EAGAIN:
 	case -ERESTARTSYS:
 	case -EINTR:
 	case -EBUSY:
@@ -358,14 +357,6 @@ vm_fault_t i915_gem_fault(struct vm_fault *vmf)
 		 * already did the job.
 		 */
 		return VM_FAULT_NOPAGE;
-	case -ENOMEM:
-		return VM_FAULT_OOM;
-	case -ENOSPC:
-	case -EFAULT:
-		return VM_FAULT_SIGBUS;
-	default:
-		WARN_ONCE(ret, "unhandled error in %s: %i\n", __func__, ret);
-		return VM_FAULT_SIGBUS;
 	}
 }
 
-- 
2.23.0

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

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

* [PATCH 05/21] drm/i915: Replace obj->pin_global with obj->frontbuffer
  2019-09-02  4:02 [PATCH 01/21] drm/i915: Restrict the aliasing-ppgtt to the size of the ggtt Chris Wilson
                   ` (2 preceding siblings ...)
  2019-09-02  4:02 ` [PATCH 04/21] drm/i915: Refresh the errno to vmf_fault translations Chris Wilson
@ 2019-09-02  4:02 ` Chris Wilson
  2019-09-02  4:02 ` [PATCH 06/21] dma-fence: Serialise signal enabling (dma_fence_enable_sw_signaling) Chris Wilson
                   ` (19 subsequent siblings)
  23 siblings, 0 replies; 53+ messages in thread
From: Chris Wilson @ 2019-09-02  4:02 UTC (permalink / raw)
  To: intel-gfx

obj->pin_global was original used as a means to keep the shrinker off
the active scanout, but we use the vma->pin_count itself for that and
the obj->frontbuffer to delay shrinking active framebuffers. The other
role that obj->pin_global gained was for spotting display objects inside
GEM and working harder to keep those coherent; for which we can again
simply inspect obj->frontbuffer directly.

Coming up next, we will want to manipulate the pin_global counter
outside of the principle locks, so would need to make pin_global atomic.
However, since obj->frontbuffer is already managed atomically, it makes
sense to use that the primary key for display objects instead of having
pin_global.

Ville pointed out the principle difference is that obj->frontbuffer is
set for as long as an intel_framebuffer is attached to an object, but
obj->pin_global was only raised for as long as the object was active. In
practice, this means that we consider the object as being on the scanout
for longer than is strictly required, causing us to be more proactive in
flushing -- though it should be true that we would have flushed
eventually when the back became the front, except that on the flip path
that flush is async but when hit from another ioctl it will be
synchronous.

v2: i915_gem_object_is_framebuffer()

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Mika Kuoppala <mika.kuoppala@linux.intel.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
---
 drivers/gpu/drm/i915/display/intel_display.c  |  2 ++
 .../gpu/drm/i915/display/intel_frontbuffer.c  | 13 +++++--
 drivers/gpu/drm/i915/gem/i915_gem_domain.c    | 34 ++++++-------------
 drivers/gpu/drm/i915/gem/i915_gem_object.h    |  3 +-
 .../gpu/drm/i915/gem/i915_gem_object_types.h  |  2 --
 drivers/gpu/drm/i915/gem/i915_gem_shrinker.c  | 15 +++-----
 drivers/gpu/drm/i915/i915_debugfs.c           | 12 ++-----
 7 files changed, 31 insertions(+), 50 deletions(-)

diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c
index e661e2099118..5e3b22e3f61d 100644
--- a/drivers/gpu/drm/i915/display/intel_display.c
+++ b/drivers/gpu/drm/i915/display/intel_display.c
@@ -2080,6 +2080,8 @@ intel_pin_and_fence_fb_obj(struct drm_framebuffer *fb,
 	u32 alignment;
 
 	WARN_ON(!mutex_is_locked(&dev->struct_mutex));
+	if (WARN_ON(!i915_gem_object_is_framebuffer(obj)))
+		return ERR_PTR(-EINVAL);
 
 	alignment = intel_surf_alignment(fb, 0);
 
diff --git a/drivers/gpu/drm/i915/display/intel_frontbuffer.c b/drivers/gpu/drm/i915/display/intel_frontbuffer.c
index 719379774fa5..fc40dc1fdbcc 100644
--- a/drivers/gpu/drm/i915/display/intel_frontbuffer.c
+++ b/drivers/gpu/drm/i915/display/intel_frontbuffer.c
@@ -220,11 +220,18 @@ static void frontbuffer_release(struct kref *ref)
 {
 	struct intel_frontbuffer *front =
 		container_of(ref, typeof(*front), ref);
+	struct drm_i915_gem_object *obj = front->obj;
+	struct i915_vma *vma;
 
-	front->obj->frontbuffer = NULL;
-	spin_unlock(&to_i915(front->obj->base.dev)->fb_tracking.lock);
+	spin_lock(&obj->vma.lock);
+	for_each_ggtt_vma(vma, obj)
+		vma->display_alignment = I915_GTT_MIN_ALIGNMENT;
+	spin_unlock(&obj->vma.lock);
 
-	i915_gem_object_put(front->obj);
+	obj->frontbuffer = NULL;
+	spin_unlock(&to_i915(obj->base.dev)->fb_tracking.lock);
+
+	i915_gem_object_put(obj);
 	kfree(front);
 }
 
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_domain.c b/drivers/gpu/drm/i915/gem/i915_gem_domain.c
index 9c58e8fac1d9..6af740a5e3db 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_domain.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_domain.c
@@ -27,7 +27,7 @@ static void __i915_gem_object_flush_for_display(struct drm_i915_gem_object *obj)
 
 void i915_gem_object_flush_if_display(struct drm_i915_gem_object *obj)
 {
-	if (!READ_ONCE(obj->pin_global))
+	if (!i915_gem_object_is_framebuffer(obj))
 		return;
 
 	i915_gem_object_lock(obj);
@@ -422,12 +422,8 @@ i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj,
 
 	assert_object_held(obj);
 
-	/* Mark the global pin early so that we account for the
-	 * display coherency whilst setting up the cache domains.
-	 */
-	obj->pin_global++;
-
-	/* The display engine is not coherent with the LLC cache on gen6.  As
+	/*
+	 * The display engine is not coherent with the LLC cache on gen6.  As
 	 * a result, we make sure that the pinning that is about to occur is
 	 * done with uncached PTEs. This is lowest common denominator for all
 	 * chipsets.
@@ -439,12 +435,11 @@ i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj,
 	ret = i915_gem_object_set_cache_level(obj,
 					      HAS_WT(to_i915(obj->base.dev)) ?
 					      I915_CACHE_WT : I915_CACHE_NONE);
-	if (ret) {
-		vma = ERR_PTR(ret);
-		goto err_unpin_global;
-	}
+	if (ret)
+		return ERR_PTR(ret);
 
-	/* As the user may map the buffer once pinned in the display plane
+	/*
+	 * As the user may map the buffer once pinned in the display plane
 	 * (e.g. libkms for the bootup splash), we have to ensure that we
 	 * always use map_and_fenceable for all scanout buffers. However,
 	 * it may simply be too big to fit into mappable, in which case
@@ -461,22 +456,19 @@ i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj,
 	if (IS_ERR(vma))
 		vma = i915_gem_object_ggtt_pin(obj, view, 0, alignment, flags);
 	if (IS_ERR(vma))
-		goto err_unpin_global;
+		return vma;
 
 	vma->display_alignment = max_t(u64, vma->display_alignment, alignment);
 
 	__i915_gem_object_flush_for_display(obj);
 
-	/* It should now be out of any other write domains, and we can update
+	/*
+	 * It should now be out of any other write domains, and we can update
 	 * the domain values for our changes.
 	 */
 	obj->read_domains |= I915_GEM_DOMAIN_GTT;
 
 	return vma;
-
-err_unpin_global:
-	obj->pin_global--;
-	return vma;
 }
 
 static void i915_gem_object_bump_inactive_ggtt(struct drm_i915_gem_object *obj)
@@ -514,12 +506,6 @@ i915_gem_object_unpin_from_display_plane(struct i915_vma *vma)
 
 	assert_object_held(obj);
 
-	if (WARN_ON(obj->pin_global == 0))
-		return;
-
-	if (--obj->pin_global == 0)
-		vma->display_alignment = I915_GTT_MIN_ALIGNMENT;
-
 	/* Bump the LRU to try and avoid premature eviction whilst flipping  */
 	i915_gem_object_bump_inactive_ggtt(obj);
 
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.h b/drivers/gpu/drm/i915/gem/i915_gem_object.h
index 5efb9936e05b..29b9eddc4c7f 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_object.h
+++ b/drivers/gpu/drm/i915/gem/i915_gem_object.h
@@ -406,7 +406,8 @@ static inline bool cpu_write_needs_clflush(struct drm_i915_gem_object *obj)
 	if (!(obj->cache_coherent & I915_BO_CACHE_COHERENT_FOR_WRITE))
 		return true;
 
-	return obj->pin_global; /* currently in use by HW, keep flushed */
+	/* Currently in use by HW (display engine)? Keep flushed. */
+	return i915_gem_object_is_framebuffer(obj);
 }
 
 static inline void __start_cpu_write(struct drm_i915_gem_object *obj)
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 ede0eb4218a8..13b9dc0e1a89 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_object_types.h
+++ b/drivers/gpu/drm/i915/gem/i915_gem_object_types.h
@@ -152,8 +152,6 @@ struct drm_i915_gem_object {
 
 	/** Count of VMA actually bound by this object */
 	atomic_t bind_count;
-	/** Count of how many global VMA are currently pinned for use by HW */
-	unsigned int pin_global;
 
 	struct {
 		struct mutex lock; /* protects the pages and their use */
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_shrinker.c b/drivers/gpu/drm/i915/gem/i915_gem_shrinker.c
index edd21d14e64f..4e55cfc2b0dc 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_shrinker.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_shrinker.c
@@ -61,7 +61,8 @@ static bool can_release_pages(struct drm_i915_gem_object *obj)
 	if (!i915_gem_object_is_shrinkable(obj))
 		return false;
 
-	/* Only report true if by unbinding the object and putting its pages
+	/*
+	 * Only report true if by unbinding the object and putting its pages
 	 * we can actually make forward progress towards freeing physical
 	 * pages.
 	 *
@@ -72,16 +73,8 @@ static bool can_release_pages(struct drm_i915_gem_object *obj)
 	if (atomic_read(&obj->mm.pages_pin_count) > atomic_read(&obj->bind_count))
 		return false;
 
-	/* If any vma are "permanently" pinned, it will prevent us from
-	 * reclaiming the obj->mm.pages. We only allow scanout objects to claim
-	 * a permanent pin, along with a few others like the context objects.
-	 * To simplify the scan, and to avoid walking the list of vma under the
-	 * object, we just check the count of its permanently pinned.
-	 */
-	if (READ_ONCE(obj->pin_global))
-		return false;
-
-	/* We can only return physical pages to the system if we can either
+	/*
+	 * We can only return physical pages to the system if we can either
 	 * discard the contents (because the user has marked them as being
 	 * purgeable) or if we can move their contents out to swap.
 	 */
diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
index 5e81c4fc13ae..30b0b592e20d 100644
--- a/drivers/gpu/drm/i915/i915_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
@@ -77,11 +77,6 @@ static int i915_capabilities(struct seq_file *m, void *data)
 	return 0;
 }
 
-static char get_pin_flag(struct drm_i915_gem_object *obj)
-{
-	return obj->pin_global ? 'p' : ' ';
-}
-
 static char get_tiling_flag(struct drm_i915_gem_object *obj)
 {
 	switch (i915_gem_object_get_tiling(obj)) {
@@ -140,9 +135,8 @@ describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj)
 	struct i915_vma *vma;
 	int pin_count = 0;
 
-	seq_printf(m, "%pK: %c%c%c%c %8zdKiB %02x %02x %s%s%s",
+	seq_printf(m, "%pK: %c%c%c %8zdKiB %02x %02x %s%s%s",
 		   &obj->base,
-		   get_pin_flag(obj),
 		   get_tiling_flag(obj),
 		   get_global_flag(obj),
 		   get_pin_mapped_flag(obj),
@@ -221,8 +215,8 @@ describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj)
 	seq_printf(m, " (pinned x %d)", pin_count);
 	if (obj->stolen)
 		seq_printf(m, " (stolen: %08llx)", obj->stolen->start);
-	if (obj->pin_global)
-		seq_printf(m, " (global)");
+	if (i915_gem_object_is_framebuffer(obj))
+		seq_printf(m, " (fb)");
 
 	engine = i915_gem_object_last_write_engine(obj);
 	if (engine)
-- 
2.23.0

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

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

* [PATCH 06/21] dma-fence: Serialise signal enabling (dma_fence_enable_sw_signaling)
  2019-09-02  4:02 [PATCH 01/21] drm/i915: Restrict the aliasing-ppgtt to the size of the ggtt Chris Wilson
                   ` (3 preceding siblings ...)
  2019-09-02  4:02 ` [PATCH 05/21] drm/i915: Replace obj->pin_global with obj->frontbuffer Chris Wilson
@ 2019-09-02  4:02 ` Chris Wilson
  2019-09-02  4:02 ` [PATCH 07/21] drm/mm: Pack allocated/scanned boolean into a bitfield Chris Wilson
                   ` (18 subsequent siblings)
  23 siblings, 0 replies; 53+ messages in thread
From: Chris Wilson @ 2019-09-02  4:02 UTC (permalink / raw)
  To: intel-gfx

Make dma_fence_enable_sw_signaling() behave like its
dma_fence_add_callback() and dma_fence_default_wait() counterparts and
perform the test to enable signaling under the fence->lock, along with
the action to do so. This ensure that should an implementation be trying
to flush the cb_list (by signaling) on retirement before freeing the
fence, it can do so in a race-free manner.

See also 0fc89b6802ba ("dma-fence: Simply wrap dma_fence_signal_locked
with dma_fence_signal").

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 drivers/dma-buf/dma-fence.c | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/drivers/dma-buf/dma-fence.c b/drivers/dma-buf/dma-fence.c
index 2c136aee3e79..587727089134 100644
--- a/drivers/dma-buf/dma-fence.c
+++ b/drivers/dma-buf/dma-fence.c
@@ -285,19 +285,18 @@ void dma_fence_enable_sw_signaling(struct dma_fence *fence)
 {
 	unsigned long flags;
 
+	if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags))
+		return;
+
+	spin_lock_irqsave(fence->lock, flags);
 	if (!test_and_set_bit(DMA_FENCE_FLAG_ENABLE_SIGNAL_BIT,
 			      &fence->flags) &&
-	    !test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags) &&
 	    fence->ops->enable_signaling) {
 		trace_dma_fence_enable_signal(fence);
-
-		spin_lock_irqsave(fence->lock, flags);
-
 		if (!fence->ops->enable_signaling(fence))
 			dma_fence_signal_locked(fence);
-
-		spin_unlock_irqrestore(fence->lock, flags);
 	}
+	spin_unlock_irqrestore(fence->lock, flags);
 }
 EXPORT_SYMBOL(dma_fence_enable_sw_signaling);
 
-- 
2.23.0

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

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

* [PATCH 07/21] drm/mm: Pack allocated/scanned boolean into a bitfield
  2019-09-02  4:02 [PATCH 01/21] drm/i915: Restrict the aliasing-ppgtt to the size of the ggtt Chris Wilson
                   ` (4 preceding siblings ...)
  2019-09-02  4:02 ` [PATCH 06/21] dma-fence: Serialise signal enabling (dma_fence_enable_sw_signaling) Chris Wilson
@ 2019-09-02  4:02 ` Chris Wilson
  2019-09-02  4:02 ` [PATCH 08/21] drm/i915: Make shrink/unshrink be atomic Chris Wilson
                   ` (17 subsequent siblings)
  23 siblings, 0 replies; 53+ messages in thread
From: Chris Wilson @ 2019-09-02  4:02 UTC (permalink / raw)
  To: intel-gfx

The ulterior motive to switching the booleans over to bitops is to
allow use of the allocated flag as a bitlock.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 drivers/gpu/drm/drm_mm.c                      | 36 +++++++++++--------
 .../gpu/drm/i915/gem/i915_gem_execbuffer.c    |  6 ++--
 drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c      |  2 +-
 drivers/gpu/drm/i915/i915_gem.c               | 16 ++++-----
 drivers/gpu/drm/i915/i915_gem_evict.c         |  2 +-
 drivers/gpu/drm/i915/i915_gem_gtt.c           |  2 +-
 drivers/gpu/drm/i915/i915_vma.c               |  4 +--
 drivers/gpu/drm/i915/i915_vma.h               |  2 +-
 drivers/gpu/drm/selftests/test-drm_mm.c       | 14 ++++----
 drivers/gpu/drm/vc4/vc4_crtc.c                |  2 +-
 drivers/gpu/drm/vc4/vc4_hvs.c                 |  2 +-
 drivers/gpu/drm/vc4/vc4_plane.c               |  4 +--
 include/drm/drm_mm.h                          |  7 ++--
 13 files changed, 53 insertions(+), 46 deletions(-)

diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c
index 4581c5387372..211967006cec 100644
--- a/drivers/gpu/drm/drm_mm.c
+++ b/drivers/gpu/drm/drm_mm.c
@@ -174,7 +174,7 @@ static void drm_mm_interval_tree_add_node(struct drm_mm_node *hole_node,
 
 	node->__subtree_last = LAST(node);
 
-	if (hole_node->allocated) {
+	if (drm_mm_node_allocated(hole_node)) {
 		rb = &hole_node->rb;
 		while (rb) {
 			parent = rb_entry(rb, struct drm_mm_node, rb);
@@ -424,9 +424,9 @@ int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node)
 
 	node->mm = mm;
 
+	__set_bit(DRM_MM_NODE_ALLOCATED_BIT, &node->flags);
 	list_add(&node->node_list, &hole->node_list);
 	drm_mm_interval_tree_add_node(hole, node);
-	node->allocated = true;
 	node->hole_size = 0;
 
 	rm_hole(hole);
@@ -543,9 +543,9 @@ int drm_mm_insert_node_in_range(struct drm_mm * const mm,
 		node->color = color;
 		node->hole_size = 0;
 
+		__set_bit(DRM_MM_NODE_ALLOCATED_BIT, &node->flags);
 		list_add(&node->node_list, &hole->node_list);
 		drm_mm_interval_tree_add_node(hole, node);
-		node->allocated = true;
 
 		rm_hole(hole);
 		if (adj_start > hole_start)
@@ -561,6 +561,11 @@ int drm_mm_insert_node_in_range(struct drm_mm * const mm,
 }
 EXPORT_SYMBOL(drm_mm_insert_node_in_range);
 
+static inline bool drm_mm_node_scanned_block(const struct drm_mm_node *node)
+{
+	return test_bit(DRM_MM_NODE_SCANNED_BIT, &node->flags);
+}
+
 /**
  * drm_mm_remove_node - Remove a memory node from the allocator.
  * @node: drm_mm_node to remove
@@ -574,8 +579,8 @@ void drm_mm_remove_node(struct drm_mm_node *node)
 	struct drm_mm *mm = node->mm;
 	struct drm_mm_node *prev_node;
 
-	DRM_MM_BUG_ON(!node->allocated);
-	DRM_MM_BUG_ON(node->scanned_block);
+	DRM_MM_BUG_ON(!drm_mm_node_allocated(node));
+	DRM_MM_BUG_ON(drm_mm_node_scanned_block(node));
 
 	prev_node = list_prev_entry(node, node_list);
 
@@ -584,11 +589,12 @@ void drm_mm_remove_node(struct drm_mm_node *node)
 
 	drm_mm_interval_tree_remove(node, &mm->interval_tree);
 	list_del(&node->node_list);
-	node->allocated = false;
 
 	if (drm_mm_hole_follows(prev_node))
 		rm_hole(prev_node);
 	add_hole(prev_node);
+
+	clear_bit_unlock(DRM_MM_NODE_ALLOCATED_BIT, &node->flags);
 }
 EXPORT_SYMBOL(drm_mm_remove_node);
 
@@ -605,7 +611,7 @@ void drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new)
 {
 	struct drm_mm *mm = old->mm;
 
-	DRM_MM_BUG_ON(!old->allocated);
+	DRM_MM_BUG_ON(!drm_mm_node_allocated(old));
 
 	*new = *old;
 
@@ -622,8 +628,7 @@ void drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new)
 				&mm->holes_addr);
 	}
 
-	old->allocated = false;
-	new->allocated = true;
+	clear_bit_unlock(DRM_MM_NODE_ALLOCATED_BIT, &old->flags);
 }
 EXPORT_SYMBOL(drm_mm_replace_node);
 
@@ -731,9 +736,9 @@ bool drm_mm_scan_add_block(struct drm_mm_scan *scan,
 	u64 adj_start, adj_end;
 
 	DRM_MM_BUG_ON(node->mm != mm);
-	DRM_MM_BUG_ON(!node->allocated);
-	DRM_MM_BUG_ON(node->scanned_block);
-	node->scanned_block = true;
+	DRM_MM_BUG_ON(!drm_mm_node_allocated(node));
+	DRM_MM_BUG_ON(drm_mm_node_scanned_block(node));
+	__set_bit(DRM_MM_NODE_SCANNED_BIT, &node->flags);
 	mm->scan_active++;
 
 	/* Remove this block from the node_list so that we enlarge the hole
@@ -818,8 +823,7 @@ bool drm_mm_scan_remove_block(struct drm_mm_scan *scan,
 	struct drm_mm_node *prev_node;
 
 	DRM_MM_BUG_ON(node->mm != scan->mm);
-	DRM_MM_BUG_ON(!node->scanned_block);
-	node->scanned_block = false;
+	DRM_MM_BUG_ON(!drm_mm_node_scanned_block(node));
 
 	DRM_MM_BUG_ON(!node->mm->scan_active);
 	node->mm->scan_active--;
@@ -837,6 +841,8 @@ bool drm_mm_scan_remove_block(struct drm_mm_scan *scan,
 		      list_next_entry(node, node_list));
 	list_add(&node->node_list, &prev_node->node_list);
 
+	__clear_bit(DRM_MM_NODE_SCANNED_BIT, &node->flags);
+
 	return (node->start + node->size > scan->hit_start &&
 		node->start < scan->hit_end);
 }
@@ -917,7 +923,7 @@ void drm_mm_init(struct drm_mm *mm, u64 start, u64 size)
 
 	/* Clever trick to avoid a special case in the free hole tracking. */
 	INIT_LIST_HEAD(&mm->head_node.node_list);
-	mm->head_node.allocated = false;
+	mm->head_node.flags = 0;
 	mm->head_node.mm = mm;
 	mm->head_node.start = start + size;
 	mm->head_node.size = -size;
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
index 27dbcb508055..c049199a1df5 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
@@ -906,7 +906,7 @@ static void reloc_cache_init(struct reloc_cache *cache,
 	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.allocated = false;
+	cache->node.flags = 0;
 	cache->ce = NULL;
 	cache->rq = NULL;
 	cache->rq_size = 0;
@@ -968,7 +968,7 @@ static void reloc_cache_reset(struct reloc_cache *cache)
 		intel_gt_flush_ggtt_writes(ggtt->vm.gt);
 		io_mapping_unmap_atomic((void __iomem *)vaddr);
 
-		if (cache->node.allocated) {
+		if (drm_mm_node_allocated(&cache->node)) {
 			ggtt->vm.clear_range(&ggtt->vm,
 					     cache->node.start,
 					     cache->node.size);
@@ -1061,7 +1061,7 @@ static void *reloc_iomap(struct drm_i915_gem_object *obj,
 	}
 
 	offset = cache->node.start;
-	if (cache->node.allocated) {
+	if (drm_mm_node_allocated(&cache->node)) {
 		ggtt->vm.insert_page(&ggtt->vm,
 				     i915_gem_object_get_dma_address(obj, page),
 				     offset, I915_CACHE_NONE, 0);
diff --git a/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c b/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c
index 296a82603be0..07fc6f28abcd 100644
--- a/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c
+++ b/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c
@@ -401,7 +401,7 @@ static u32 uc_fw_ggtt_offset(struct intel_uc_fw *uc_fw, struct i915_ggtt *ggtt)
 {
 	struct drm_mm_node *node = &ggtt->uc_fw;
 
-	GEM_BUG_ON(!node->allocated);
+	GEM_BUG_ON(!drm_mm_node_allocated(node));
 	GEM_BUG_ON(upper_32_bits(node->start));
 	GEM_BUG_ON(upper_32_bits(node->start + node->size - 1));
 
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 95e7c52cf8ed..814f62fca727 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -351,12 +351,12 @@ i915_gem_gtt_pread(struct drm_i915_gem_object *obj,
 					       PIN_NOEVICT);
 	if (!IS_ERR(vma)) {
 		node.start = i915_ggtt_offset(vma);
-		node.allocated = false;
+		node.flags = 0;
 	} else {
 		ret = insert_mappable_node(ggtt, &node, PAGE_SIZE);
 		if (ret)
 			goto out_unlock;
-		GEM_BUG_ON(!node.allocated);
+		GEM_BUG_ON(!drm_mm_node_allocated(&node));
 	}
 
 	mutex_unlock(&i915->drm.struct_mutex);
@@ -393,7 +393,7 @@ i915_gem_gtt_pread(struct drm_i915_gem_object *obj,
 		unsigned page_offset = offset_in_page(offset);
 		unsigned page_length = PAGE_SIZE - page_offset;
 		page_length = remain < page_length ? remain : page_length;
-		if (node.allocated) {
+		if (drm_mm_node_allocated(&node)) {
 			ggtt->vm.insert_page(&ggtt->vm,
 					     i915_gem_object_get_dma_address(obj, offset >> PAGE_SHIFT),
 					     node.start, I915_CACHE_NONE, 0);
@@ -415,7 +415,7 @@ i915_gem_gtt_pread(struct drm_i915_gem_object *obj,
 	i915_gem_object_unlock_fence(obj, fence);
 out_unpin:
 	mutex_lock(&i915->drm.struct_mutex);
-	if (node.allocated) {
+	if (drm_mm_node_allocated(&node)) {
 		ggtt->vm.clear_range(&ggtt->vm, node.start, node.size);
 		remove_mappable_node(&node);
 	} else {
@@ -561,12 +561,12 @@ i915_gem_gtt_pwrite_fast(struct drm_i915_gem_object *obj,
 					       PIN_NOEVICT);
 	if (!IS_ERR(vma)) {
 		node.start = i915_ggtt_offset(vma);
-		node.allocated = false;
+		node.flags = 0;
 	} else {
 		ret = insert_mappable_node(ggtt, &node, PAGE_SIZE);
 		if (ret)
 			goto out_rpm;
-		GEM_BUG_ON(!node.allocated);
+		GEM_BUG_ON(!drm_mm_node_allocated(&node));
 	}
 
 	mutex_unlock(&i915->drm.struct_mutex);
@@ -604,7 +604,7 @@ i915_gem_gtt_pwrite_fast(struct drm_i915_gem_object *obj,
 		unsigned int page_offset = offset_in_page(offset);
 		unsigned int page_length = PAGE_SIZE - page_offset;
 		page_length = remain < page_length ? remain : page_length;
-		if (node.allocated) {
+		if (drm_mm_node_allocated(&node)) {
 			/* flush the write before we modify the GGTT */
 			intel_gt_flush_ggtt_writes(ggtt->vm.gt);
 			ggtt->vm.insert_page(&ggtt->vm,
@@ -636,7 +636,7 @@ i915_gem_gtt_pwrite_fast(struct drm_i915_gem_object *obj,
 out_unpin:
 	mutex_lock(&i915->drm.struct_mutex);
 	intel_gt_flush_ggtt_writes(ggtt->vm.gt);
-	if (node.allocated) {
+	if (drm_mm_node_allocated(&node)) {
 		ggtt->vm.clear_range(&ggtt->vm, node.start, node.size);
 		remove_mappable_node(&node);
 	} else {
diff --git a/drivers/gpu/drm/i915/i915_gem_evict.c b/drivers/gpu/drm/i915/i915_gem_evict.c
index 52c86c6e0673..7abcac3b5e2e 100644
--- a/drivers/gpu/drm/i915/i915_gem_evict.c
+++ b/drivers/gpu/drm/i915/i915_gem_evict.c
@@ -301,7 +301,7 @@ int i915_gem_evict_for_node(struct i915_address_space *vm,
 			break;
 		}
 
-		GEM_BUG_ON(!node->allocated);
+		GEM_BUG_ON(!drm_mm_node_allocated(node));
 		vma = container_of(node, typeof(*vma), node);
 
 		/* If we are using coloring to insert guard pages between
diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
index 906dc6fff383..27e8b12b9e45 100644
--- a/drivers/gpu/drm/i915/i915_gem_gtt.c
+++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
@@ -2552,7 +2552,7 @@ static void i915_gtt_color_adjust(const struct drm_mm_node *node,
 				  u64 *start,
 				  u64 *end)
 {
-	if (node->allocated && node->color != color)
+	if (drm_mm_node_allocated(node) && node->color != color)
 		*start += I915_GTT_PAGE_SIZE;
 
 	/* Also leave a space between the unallocated reserved node after the
diff --git a/drivers/gpu/drm/i915/i915_vma.c b/drivers/gpu/drm/i915/i915_vma.c
index e0e677b2a3a9..76525543009a 100644
--- a/drivers/gpu/drm/i915/i915_vma.c
+++ b/drivers/gpu/drm/i915/i915_vma.c
@@ -479,7 +479,7 @@ void __i915_vma_set_map_and_fenceable(struct i915_vma *vma)
 
 static bool color_differs(struct drm_mm_node *node, unsigned long color)
 {
-	return node->allocated && node->color != color;
+	return drm_mm_node_allocated(node) && node->color != color;
 }
 
 bool i915_gem_valid_gtt_space(struct i915_vma *vma, unsigned long cache_level)
@@ -797,7 +797,7 @@ void i915_vma_reopen(struct i915_vma *vma)
 
 static void __i915_vma_destroy(struct i915_vma *vma)
 {
-	GEM_BUG_ON(vma->node.allocated);
+	GEM_BUG_ON(drm_mm_node_allocated(&vma->node));
 	GEM_BUG_ON(vma->fence);
 
 	mutex_lock(&vma->vm->mutex);
diff --git a/drivers/gpu/drm/i915/i915_vma.h b/drivers/gpu/drm/i915/i915_vma.h
index 889fc7cb910a..af2ef0a51455 100644
--- a/drivers/gpu/drm/i915/i915_vma.h
+++ b/drivers/gpu/drm/i915/i915_vma.h
@@ -214,7 +214,7 @@ static inline bool i915_vma_is_closed(const struct i915_vma *vma)
 static inline u32 i915_ggtt_offset(const struct i915_vma *vma)
 {
 	GEM_BUG_ON(!i915_vma_is_ggtt(vma));
-	GEM_BUG_ON(!vma->node.allocated);
+	GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
 	GEM_BUG_ON(upper_32_bits(vma->node.start));
 	GEM_BUG_ON(upper_32_bits(vma->node.start + vma->node.size - 1));
 	return lower_32_bits(vma->node.start);
diff --git a/drivers/gpu/drm/selftests/test-drm_mm.c b/drivers/gpu/drm/selftests/test-drm_mm.c
index 388f9844f4ba..9aabe82dcd3a 100644
--- a/drivers/gpu/drm/selftests/test-drm_mm.c
+++ b/drivers/gpu/drm/selftests/test-drm_mm.c
@@ -854,7 +854,7 @@ static bool assert_contiguous_in_range(struct drm_mm *mm,
 
 	if (start > 0) {
 		node = __drm_mm_interval_first(mm, 0, start - 1);
-		if (node->allocated) {
+		if (drm_mm_node_allocated(node)) {
 			pr_err("node before start: node=%llx+%llu, start=%llx\n",
 			       node->start, node->size, start);
 			return false;
@@ -863,7 +863,7 @@ static bool assert_contiguous_in_range(struct drm_mm *mm,
 
 	if (end < U64_MAX) {
 		node = __drm_mm_interval_first(mm, end, U64_MAX);
-		if (node->allocated) {
+		if (drm_mm_node_allocated(node)) {
 			pr_err("node after end: node=%llx+%llu, end=%llx\n",
 			       node->start, node->size, end);
 			return false;
@@ -1156,12 +1156,12 @@ static void show_holes(const struct drm_mm *mm, int count)
 		struct drm_mm_node *next = list_next_entry(hole, node_list);
 		const char *node1 = NULL, *node2 = NULL;
 
-		if (hole->allocated)
+		if (drm_mm_node_allocated(hole))
 			node1 = kasprintf(GFP_KERNEL,
 					  "[%llx + %lld, color=%ld], ",
 					  hole->start, hole->size, hole->color);
 
-		if (next->allocated)
+		if (drm_mm_node_allocated(next))
 			node2 = kasprintf(GFP_KERNEL,
 					  ", [%llx + %lld, color=%ld]",
 					  next->start, next->size, next->color);
@@ -1900,18 +1900,18 @@ static void separate_adjacent_colors(const struct drm_mm_node *node,
 				     u64 *start,
 				     u64 *end)
 {
-	if (node->allocated && node->color != color)
+	if (drm_mm_node_allocated(node) && node->color != color)
 		++*start;
 
 	node = list_next_entry(node, node_list);
-	if (node->allocated && node->color != color)
+	if (drm_mm_node_allocated(node) && node->color != color)
 		--*end;
 }
 
 static bool colors_abutt(const struct drm_mm_node *node)
 {
 	if (!drm_mm_hole_follows(node) &&
-	    list_next_entry(node, node_list)->allocated) {
+	    drm_mm_node_allocated(list_next_entry(node, node_list))) {
 		pr_err("colors abutt; %ld [%llx + %llx] is next to %ld [%llx + %llx]!\n",
 		       node->color, node->start, node->size,
 		       list_next_entry(node, node_list)->color,
diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c
index f1f0a7c87771..b00e20f5ce05 100644
--- a/drivers/gpu/drm/vc4/vc4_crtc.c
+++ b/drivers/gpu/drm/vc4/vc4_crtc.c
@@ -994,7 +994,7 @@ static void vc4_crtc_destroy_state(struct drm_crtc *crtc,
 	struct vc4_dev *vc4 = to_vc4_dev(crtc->dev);
 	struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state);
 
-	if (vc4_state->mm.allocated) {
+	if (drm_mm_node_allocated(&vc4_state->mm)) {
 		unsigned long flags;
 
 		spin_lock_irqsave(&vc4->hvs->mm_lock, flags);
diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c
index 9936b15d0bf1..5a43659da319 100644
--- a/drivers/gpu/drm/vc4/vc4_hvs.c
+++ b/drivers/gpu/drm/vc4/vc4_hvs.c
@@ -315,7 +315,7 @@ static void vc4_hvs_unbind(struct device *dev, struct device *master,
 	struct drm_device *drm = dev_get_drvdata(master);
 	struct vc4_dev *vc4 = drm->dev_private;
 
-	if (vc4->hvs->mitchell_netravali_filter.allocated)
+	if (drm_mm_node_allocated(&vc4->hvs->mitchell_netravali_filter))
 		drm_mm_remove_node(&vc4->hvs->mitchell_netravali_filter);
 
 	drm_mm_takedown(&vc4->hvs->dlist_mm);
diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c
index 5e5f90810aca..4934127f0d76 100644
--- a/drivers/gpu/drm/vc4/vc4_plane.c
+++ b/drivers/gpu/drm/vc4/vc4_plane.c
@@ -178,7 +178,7 @@ static void vc4_plane_destroy_state(struct drm_plane *plane,
 	struct vc4_dev *vc4 = to_vc4_dev(plane->dev);
 	struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
 
-	if (vc4_state->lbm.allocated) {
+	if (drm_mm_node_allocated(&vc4_state->lbm)) {
 		unsigned long irqflags;
 
 		spin_lock_irqsave(&vc4->hvs->mm_lock, irqflags);
@@ -557,7 +557,7 @@ static int vc4_plane_allocate_lbm(struct drm_plane_state *state)
 	/* Allocate the LBM memory that the HVS will use for temporary
 	 * storage due to our scaling/format conversion.
 	 */
-	if (!vc4_state->lbm.allocated) {
+	if (!drm_mm_node_allocated(&vc4_state->lbm)) {
 		int ret;
 
 		spin_lock_irqsave(&vc4->hvs->mm_lock, irqflags);
diff --git a/include/drm/drm_mm.h b/include/drm/drm_mm.h
index 2c3bbb43c7d1..d7939c054259 100644
--- a/include/drm/drm_mm.h
+++ b/include/drm/drm_mm.h
@@ -168,8 +168,9 @@ struct drm_mm_node {
 	struct rb_node rb_hole_addr;
 	u64 __subtree_last;
 	u64 hole_size;
-	bool allocated : 1;
-	bool scanned_block : 1;
+	unsigned long flags;
+#define DRM_MM_NODE_ALLOCATED_BIT	0
+#define DRM_MM_NODE_SCANNED_BIT		1
 #ifdef CONFIG_DRM_DEBUG_MM
 	depot_stack_handle_t stack;
 #endif
@@ -253,7 +254,7 @@ struct drm_mm_scan {
  */
 static inline bool drm_mm_node_allocated(const struct drm_mm_node *node)
 {
-	return node->allocated;
+	return test_bit(DRM_MM_NODE_ALLOCATED_BIT, &node->flags);
 }
 
 /**
-- 
2.23.0

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

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

* [PATCH 08/21] drm/i915: Make shrink/unshrink be atomic
  2019-09-02  4:02 [PATCH 01/21] drm/i915: Restrict the aliasing-ppgtt to the size of the ggtt Chris Wilson
                   ` (5 preceding siblings ...)
  2019-09-02  4:02 ` [PATCH 07/21] drm/mm: Pack allocated/scanned boolean into a bitfield Chris Wilson
@ 2019-09-02  4:02 ` Chris Wilson
  2019-09-10 19:54   ` Matthew Auld
  2019-09-02  4:02 ` [PATCH 09/21] drm/i915: Only track bound elements of the GTT Chris Wilson
                   ` (16 subsequent siblings)
  23 siblings, 1 reply; 53+ messages in thread
From: Chris Wilson @ 2019-09-02  4:02 UTC (permalink / raw)
  To: intel-gfx

Add an atomic counter and always take the spinlock around the pin/unpin
events, so that we can perform the list manipulation concurrently.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 drivers/gpu/drm/i915/gem/i915_gem_domain.c    |  3 +-
 .../gpu/drm/i915/gem/i915_gem_object_types.h  |  1 +
 drivers/gpu/drm/i915/gem/i915_gem_pages.c     |  1 +
 drivers/gpu/drm/i915/gem/i915_gem_shrinker.c  | 36 +++++++++++--------
 drivers/gpu/drm/i915/gt/intel_context.c       |  2 +-
 5 files changed, 26 insertions(+), 17 deletions(-)

diff --git a/drivers/gpu/drm/i915/gem/i915_gem_domain.c b/drivers/gpu/drm/i915/gem/i915_gem_domain.c
index 6af740a5e3db..f0c437b6e995 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_domain.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_domain.c
@@ -492,7 +492,8 @@ static void i915_gem_object_bump_inactive_ggtt(struct drm_i915_gem_object *obj)
 
 		spin_lock_irqsave(&i915->mm.obj_lock, flags);
 
-		if (obj->mm.madv == I915_MADV_WILLNEED)
+		if (obj->mm.madv == I915_MADV_WILLNEED &&
+		    !atomic_read(&obj->mm.shrink_pin))
 			list_move_tail(&obj->mm.link, &i915->mm.shrink_list);
 
 		spin_unlock_irqrestore(&i915->mm.obj_lock, flags);
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 13b9dc0e1a89..b0550727e69a 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_object_types.h
+++ b/drivers/gpu/drm/i915/gem/i915_gem_object_types.h
@@ -156,6 +156,7 @@ struct drm_i915_gem_object {
 	struct {
 		struct mutex lock; /* protects the pages and their use */
 		atomic_t pages_pin_count;
+		atomic_t shrink_pin;
 
 		struct sg_table *pages;
 		void *mapping;
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_pages.c b/drivers/gpu/drm/i915/gem/i915_gem_pages.c
index 18f0ce0135c1..2e941f093a20 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_pages.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_pages.c
@@ -71,6 +71,7 @@ void __i915_gem_object_set_pages(struct drm_i915_gem_object *obj,
 			list = &i915->mm.shrink_list;
 		list_add_tail(&obj->mm.link, list);
 
+		atomic_set(&obj->mm.shrink_pin, 0);
 		spin_unlock_irqrestore(&i915->mm.obj_lock, flags);
 	}
 }
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_shrinker.c b/drivers/gpu/drm/i915/gem/i915_gem_shrinker.c
index 4e55cfc2b0dc..d2c05d752909 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_shrinker.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_shrinker.c
@@ -516,46 +516,52 @@ void i915_gem_shrinker_taints_mutex(struct drm_i915_private *i915,
 
 void i915_gem_object_make_unshrinkable(struct drm_i915_gem_object *obj)
 {
+	struct drm_i915_private *i915 = obj_to_i915(obj);
+	unsigned long flags;
+
 	/*
 	 * We can only be called while the pages are pinned or when
 	 * the pages are released. If pinned, we should only be called
 	 * from a single caller under controlled conditions; and on release
 	 * only one caller may release us. Neither the two may cross.
 	 */
-	if (!list_empty(&obj->mm.link)) { /* pinned by caller */
-		struct drm_i915_private *i915 = obj_to_i915(obj);
-		unsigned long flags;
-
-		spin_lock_irqsave(&i915->mm.obj_lock, flags);
-		GEM_BUG_ON(list_empty(&obj->mm.link));
+	if (atomic_add_unless(&obj->mm.shrink_pin, 1, 0))
+		return;
 
+	spin_lock_irqsave(&i915->mm.obj_lock, flags);
+	if (!atomic_fetch_inc(&obj->mm.shrink_pin) &&
+	    !list_empty(&obj->mm.link)) {
 		list_del_init(&obj->mm.link);
 		i915->mm.shrink_count--;
 		i915->mm.shrink_memory -= obj->base.size;
-
-		spin_unlock_irqrestore(&i915->mm.obj_lock, flags);
 	}
+	spin_unlock_irqrestore(&i915->mm.obj_lock, flags);
 }
 
 static void __i915_gem_object_make_shrinkable(struct drm_i915_gem_object *obj,
 					      struct list_head *head)
 {
+	struct drm_i915_private *i915 = obj_to_i915(obj);
+	unsigned long flags;
+
 	GEM_BUG_ON(!i915_gem_object_has_pages(obj));
-	GEM_BUG_ON(!list_empty(&obj->mm.link));
+	if (!i915_gem_object_is_shrinkable(obj))
+		return;
 
-	if (i915_gem_object_is_shrinkable(obj)) {
-		struct drm_i915_private *i915 = obj_to_i915(obj);
-		unsigned long flags;
+	if (atomic_add_unless(&obj->mm.shrink_pin, -1, 1))
+		return;
 
-		spin_lock_irqsave(&i915->mm.obj_lock, flags);
-		GEM_BUG_ON(!kref_read(&obj->base.refcount));
+	spin_lock_irqsave(&i915->mm.obj_lock, flags);
+	GEM_BUG_ON(!kref_read(&obj->base.refcount));
+	if (atomic_dec_and_test(&obj->mm.shrink_pin)) {
+		GEM_BUG_ON(!list_empty(&obj->mm.link));
 
 		list_add_tail(&obj->mm.link, head);
 		i915->mm.shrink_count++;
 		i915->mm.shrink_memory += obj->base.size;
 
-		spin_unlock_irqrestore(&i915->mm.obj_lock, flags);
 	}
+	spin_unlock_irqrestore(&i915->mm.obj_lock, flags);
 }
 
 void i915_gem_object_make_shrinkable(struct drm_i915_gem_object *obj)
diff --git a/drivers/gpu/drm/i915/gt/intel_context.c b/drivers/gpu/drm/i915/gt/intel_context.c
index f55691d151ae..c0495811f493 100644
--- a/drivers/gpu/drm/i915/gt/intel_context.c
+++ b/drivers/gpu/drm/i915/gt/intel_context.c
@@ -134,8 +134,8 @@ static int __context_pin_state(struct i915_vma *vma)
 
 static void __context_unpin_state(struct i915_vma *vma)
 {
-	__i915_vma_unpin(vma);
 	i915_vma_make_shrinkable(vma);
+	__i915_vma_unpin(vma);
 }
 
 static void __intel_context_retire(struct i915_active *active)
-- 
2.23.0

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

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

* [PATCH 09/21] drm/i915: Only track bound elements of the GTT
  2019-09-02  4:02 [PATCH 01/21] drm/i915: Restrict the aliasing-ppgtt to the size of the ggtt Chris Wilson
                   ` (6 preceding siblings ...)
  2019-09-02  4:02 ` [PATCH 08/21] drm/i915: Make shrink/unshrink be atomic Chris Wilson
@ 2019-09-02  4:02 ` Chris Wilson
  2019-09-02  4:02 ` [PATCH 10/21] drm/i915: Make i915_vma.flags atomic_t for mutex reduction Chris Wilson
                   ` (15 subsequent siblings)
  23 siblings, 0 replies; 53+ messages in thread
From: Chris Wilson @ 2019-09-02  4:02 UTC (permalink / raw)
  To: intel-gfx; +Cc: Matthew Auld

The premise here is to simply avoiding having to acquire the vm->mutex
inside vma create/destroy to update the vm->unbound_lists, to avoid some
nasty lock recursions later.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Reviewed-by: Matthew Auld <matthew.auld@intel.com>
---
 drivers/gpu/drm/i915/gem/i915_gem_stolen.c    |  2 +-
 drivers/gpu/drm/i915/i915_gem_gtt.c           | 23 ++++---------------
 drivers/gpu/drm/i915/i915_gem_gtt.h           |  5 ----
 drivers/gpu/drm/i915/i915_vma.c               | 12 ++--------
 drivers/gpu/drm/i915/selftests/i915_gem_gtt.c |  2 +-
 5 files changed, 8 insertions(+), 36 deletions(-)

diff --git a/drivers/gpu/drm/i915/gem/i915_gem_stolen.c b/drivers/gpu/drm/i915/gem/i915_gem_stolen.c
index aa533b4ab5f5..2e1bfd5e4adf 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_stolen.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_stolen.c
@@ -689,7 +689,7 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_i915_private *dev_priv
 	__i915_vma_set_map_and_fenceable(vma);
 
 	mutex_lock(&ggtt->vm.mutex);
-	list_move_tail(&vma->vm_link, &ggtt->vm.bound_list);
+	list_add_tail(&vma->vm_link, &ggtt->vm.bound_list);
 	mutex_unlock(&ggtt->vm.mutex);
 
 	GEM_BUG_ON(i915_gem_object_is_shrinkable(obj));
diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
index 27e8b12b9e45..d28e5ffa0b9d 100644
--- a/drivers/gpu/drm/i915/i915_gem_gtt.c
+++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
@@ -505,19 +505,12 @@ static void i915_address_space_fini(struct i915_address_space *vm)
 
 static void ppgtt_destroy_vma(struct i915_address_space *vm)
 {
-	struct list_head *phases[] = {
-		&vm->bound_list,
-		&vm->unbound_list,
-		NULL,
-	}, **phase;
+	struct i915_vma *vma, *vn;
 
 	mutex_lock(&vm->i915->drm.struct_mutex);
-	for (phase = phases; *phase; phase++) {
-		struct i915_vma *vma, *vn;
-
-		list_for_each_entry_safe(vma, vn, *phase, vm_link)
-			i915_vma_destroy(vma);
-	}
+	list_for_each_entry_safe(vma, vn, &vm->bound_list, vm_link)
+		i915_vma_destroy(vma);
+	GEM_BUG_ON(!list_empty(&vm->bound_list));
 	mutex_unlock(&vm->i915->drm.struct_mutex);
 }
 
@@ -528,9 +521,6 @@ static void __i915_vm_release(struct work_struct *work)
 
 	ppgtt_destroy_vma(vm);
 
-	GEM_BUG_ON(!list_empty(&vm->bound_list));
-	GEM_BUG_ON(!list_empty(&vm->unbound_list));
-
 	vm->cleanup(vm);
 	i915_address_space_fini(vm);
 
@@ -569,7 +559,6 @@ static void i915_address_space_init(struct i915_address_space *vm, int subclass)
 
 	stash_init(&vm->free_pages);
 
-	INIT_LIST_HEAD(&vm->unbound_list);
 	INIT_LIST_HEAD(&vm->bound_list);
 }
 
@@ -1883,10 +1872,6 @@ static struct i915_vma *pd_vma_create(struct gen6_ppgtt *ppgtt, int size)
 	INIT_LIST_HEAD(&vma->obj_link);
 	INIT_LIST_HEAD(&vma->closed_link);
 
-	mutex_lock(&vma->vm->mutex);
-	list_add(&vma->vm_link, &vma->vm->unbound_list);
-	mutex_unlock(&vma->vm->mutex);
-
 	return vma;
 }
 
diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.h b/drivers/gpu/drm/i915/i915_gem_gtt.h
index 07c85c134d4c..546a7c0aa0e7 100644
--- a/drivers/gpu/drm/i915/i915_gem_gtt.h
+++ b/drivers/gpu/drm/i915/i915_gem_gtt.h
@@ -320,11 +320,6 @@ struct i915_address_space {
 	 */
 	struct list_head bound_list;
 
-	/**
-	 * List of vma that are not unbound.
-	 */
-	struct list_head unbound_list;
-
 	struct pagestash free_pages;
 
 	/* Global GTT */
diff --git a/drivers/gpu/drm/i915/i915_vma.c b/drivers/gpu/drm/i915/i915_vma.c
index 76525543009a..848605ffff2d 100644
--- a/drivers/gpu/drm/i915/i915_vma.c
+++ b/drivers/gpu/drm/i915/i915_vma.c
@@ -218,10 +218,6 @@ vma_create(struct drm_i915_gem_object *obj,
 
 	spin_unlock(&obj->vma.lock);
 
-	mutex_lock(&vm->mutex);
-	list_add(&vma->vm_link, &vm->unbound_list);
-	mutex_unlock(&vm->mutex);
-
 	return vma;
 
 err_vma:
@@ -659,7 +655,7 @@ i915_vma_insert(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
 	GEM_BUG_ON(!i915_gem_valid_gtt_space(vma, cache_level));
 
 	mutex_lock(&vma->vm->mutex);
-	list_move_tail(&vma->vm_link, &vma->vm->bound_list);
+	list_add_tail(&vma->vm_link, &vma->vm->bound_list);
 	mutex_unlock(&vma->vm->mutex);
 
 	if (vma->obj) {
@@ -687,7 +683,7 @@ i915_vma_remove(struct i915_vma *vma)
 
 	mutex_lock(&vma->vm->mutex);
 	drm_mm_remove_node(&vma->node);
-	list_move_tail(&vma->vm_link, &vma->vm->unbound_list);
+	list_del(&vma->vm_link);
 	mutex_unlock(&vma->vm->mutex);
 
 	/*
@@ -800,10 +796,6 @@ static void __i915_vma_destroy(struct i915_vma *vma)
 	GEM_BUG_ON(drm_mm_node_allocated(&vma->node));
 	GEM_BUG_ON(vma->fence);
 
-	mutex_lock(&vma->vm->mutex);
-	list_del(&vma->vm_link);
-	mutex_unlock(&vma->vm->mutex);
-
 	if (vma->obj) {
 		struct drm_i915_gem_object *obj = vma->obj;
 
diff --git a/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c b/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
index 31a51ca1ddcb..a90c9be95f8c 100644
--- a/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
+++ b/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
@@ -1237,7 +1237,7 @@ static void track_vma_bind(struct i915_vma *vma)
 	vma->pages = obj->mm.pages;
 
 	mutex_lock(&vma->vm->mutex);
-	list_move_tail(&vma->vm_link, &vma->vm->bound_list);
+	list_add_tail(&vma->vm_link, &vma->vm->bound_list);
 	mutex_unlock(&vma->vm->mutex);
 }
 
-- 
2.23.0

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

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

* [PATCH 10/21] drm/i915: Make i915_vma.flags atomic_t for mutex reduction
  2019-09-02  4:02 [PATCH 01/21] drm/i915: Restrict the aliasing-ppgtt to the size of the ggtt Chris Wilson
                   ` (7 preceding siblings ...)
  2019-09-02  4:02 ` [PATCH 09/21] drm/i915: Only track bound elements of the GTT Chris Wilson
@ 2019-09-02  4:02 ` Chris Wilson
  2019-09-10 20:06   ` Matthew Auld
  2019-09-02  4:02 ` [PATCH 11/21] drm/i915/gtt: Make sure the gen6 ppgtt is bound before first use Chris Wilson
                   ` (14 subsequent siblings)
  23 siblings, 1 reply; 53+ messages in thread
From: Chris Wilson @ 2019-09-02  4:02 UTC (permalink / raw)
  To: intel-gfx

In preparation for reducing struct_mutex stranglehold around the vm,
make the vma.flags atomic so that we can acquire a pin on the vma
atomically before deciding if we need to take the mutex.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 drivers/gpu/drm/i915/gem/i915_gem_object.c |  2 +-
 drivers/gpu/drm/i915/gem/i915_gem_stolen.c |  2 +-
 drivers/gpu/drm/i915/i915_gem_gtt.c        | 14 ++---
 drivers/gpu/drm/i915/i915_vma.c            | 29 +++++-----
 drivers/gpu/drm/i915/i915_vma.h            | 62 +++++++++++++---------
 drivers/gpu/drm/i915/selftests/mock_gtt.c  |  4 +-
 6 files changed, 64 insertions(+), 49 deletions(-)

diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.c b/drivers/gpu/drm/i915/gem/i915_gem_object.c
index d7855dc5a5c5..0ef60dae23a7 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_object.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_object.c
@@ -163,7 +163,7 @@ static void __i915_gem_free_objects(struct drm_i915_private *i915,
 
 		list_for_each_entry_safe(vma, vn, &obj->vma.list, obj_link) {
 			GEM_BUG_ON(i915_vma_is_active(vma));
-			vma->flags &= ~I915_VMA_PIN_MASK;
+			atomic_and(~I915_VMA_PIN_MASK, &vma->flags);
 			i915_vma_destroy(vma);
 		}
 		GEM_BUG_ON(!list_empty(&obj->vma.list));
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_stolen.c b/drivers/gpu/drm/i915/gem/i915_gem_stolen.c
index 2e1bfd5e4adf..0d81de1461b4 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_stolen.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_stolen.c
@@ -685,7 +685,7 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_i915_private *dev_priv
 	GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
 
 	vma->pages = obj->mm.pages;
-	vma->flags |= I915_VMA_GLOBAL_BIND;
+	set_bit(I915_VMA_GLOBAL_BIND_BIT, __i915_vma_flags(vma));
 	__i915_vma_set_map_and_fenceable(vma);
 
 	mutex_lock(&ggtt->vm.mutex);
diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
index d28e5ffa0b9d..d5bf57fa837b 100644
--- a/drivers/gpu/drm/i915/i915_gem_gtt.c
+++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
@@ -155,7 +155,7 @@ static int ppgtt_bind_vma(struct i915_vma *vma,
 	u32 pte_flags;
 	int err;
 
-	if (!(vma->flags & I915_VMA_LOCAL_BIND)) {
+	if (!i915_vma_is_bound(vma, I915_VMA_LOCAL_BIND)) {
 		err = vma->vm->allocate_va_range(vma->vm,
 						 vma->node.start, vma->size);
 		if (err)
@@ -1866,7 +1866,7 @@ static struct i915_vma *pd_vma_create(struct gen6_ppgtt *ppgtt, int size)
 
 	vma->size = size;
 	vma->fence_size = size;
-	vma->flags = I915_VMA_GGTT;
+	atomic_set(&vma->flags, I915_VMA_GGTT);
 	vma->ggtt_view.type = I915_GGTT_VIEW_ROTATED; /* prevent fencing */
 
 	INIT_LIST_HEAD(&vma->obj_link);
@@ -2425,7 +2425,7 @@ static int ggtt_bind_vma(struct i915_vma *vma,
 	 * GLOBAL/LOCAL_BIND, it's all the same ptes. Hence unconditionally
 	 * upgrade to both bound if we bind either to avoid double-binding.
 	 */
-	vma->flags |= I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND;
+	atomic_or(I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND, &vma->flags);
 
 	return 0;
 }
@@ -2455,7 +2455,7 @@ static int aliasing_gtt_bind_vma(struct i915_vma *vma,
 	if (flags & I915_VMA_LOCAL_BIND) {
 		struct i915_ppgtt *alias = i915_vm_to_ggtt(vma->vm)->alias;
 
-		if (!(vma->flags & I915_VMA_LOCAL_BIND)) {
+		if (!i915_vma_is_bound(vma, I915_VMA_LOCAL_BIND)) {
 			ret = alias->vm.allocate_va_range(&alias->vm,
 							  vma->node.start,
 							  vma->size);
@@ -2483,7 +2483,7 @@ static void aliasing_gtt_unbind_vma(struct i915_vma *vma)
 {
 	struct drm_i915_private *i915 = vma->vm->i915;
 
-	if (vma->flags & I915_VMA_GLOBAL_BIND) {
+	if (i915_vma_is_bound(vma, I915_VMA_GLOBAL_BIND)) {
 		struct i915_address_space *vm = vma->vm;
 		intel_wakeref_t wakeref;
 
@@ -2491,7 +2491,7 @@ static void aliasing_gtt_unbind_vma(struct i915_vma *vma)
 			vm->clear_range(vm, vma->node.start, vma->size);
 	}
 
-	if (vma->flags & I915_VMA_LOCAL_BIND) {
+	if (i915_vma_is_bound(vma, I915_VMA_LOCAL_BIND)) {
 		struct i915_address_space *vm =
 			&i915_vm_to_ggtt(vma->vm)->alias->vm;
 
@@ -3293,7 +3293,7 @@ static void ggtt_restore_mappings(struct i915_ggtt *ggtt)
 	list_for_each_entry_safe(vma, vn, &ggtt->vm.bound_list, vm_link) {
 		struct drm_i915_gem_object *obj = vma->obj;
 
-		if (!(vma->flags & I915_VMA_GLOBAL_BIND))
+		if (!i915_vma_is_bound(vma, I915_VMA_GLOBAL_BIND))
 			continue;
 
 		mutex_unlock(&ggtt->vm.mutex);
diff --git a/drivers/gpu/drm/i915/i915_vma.c b/drivers/gpu/drm/i915/i915_vma.c
index 848605ffff2d..9adb85ba6daa 100644
--- a/drivers/gpu/drm/i915/i915_vma.c
+++ b/drivers/gpu/drm/i915/i915_vma.c
@@ -171,7 +171,7 @@ vma_create(struct drm_i915_gem_object *obj,
 								i915_gem_object_get_stride(obj));
 		GEM_BUG_ON(!is_power_of_2(vma->fence_alignment));
 
-		vma->flags |= I915_VMA_GGTT;
+		__set_bit(I915_VMA_GGTT_BIT, __i915_vma_flags(vma));
 	}
 
 	spin_lock(&obj->vma.lock);
@@ -321,7 +321,8 @@ int i915_vma_bind(struct i915_vma *vma, enum i915_cache_level cache_level,
 	if (flags & PIN_USER)
 		bind_flags |= I915_VMA_LOCAL_BIND;
 
-	vma_flags = vma->flags & (I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND);
+	vma_flags = atomic_read(&vma->flags);
+	vma_flags &= I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND;
 	if (flags & PIN_UPDATE)
 		bind_flags |= vma_flags;
 	else
@@ -336,7 +337,7 @@ int i915_vma_bind(struct i915_vma *vma, enum i915_cache_level cache_level,
 	if (ret)
 		return ret;
 
-	vma->flags |= bind_flags;
+	atomic_or(bind_flags, &vma->flags);
 	return 0;
 }
 
@@ -355,7 +356,7 @@ void __iomem *i915_vma_pin_iomap(struct i915_vma *vma)
 	}
 
 	GEM_BUG_ON(!i915_vma_is_ggtt(vma));
-	GEM_BUG_ON((vma->flags & I915_VMA_GLOBAL_BIND) == 0);
+	GEM_BUG_ON(!i915_vma_is_bound(vma, I915_VMA_GLOBAL_BIND));
 
 	ptr = vma->iomap;
 	if (ptr == NULL) {
@@ -468,9 +469,9 @@ void __i915_vma_set_map_and_fenceable(struct i915_vma *vma)
 	mappable = vma->node.start + vma->fence_size <= i915_vm_to_ggtt(vma->vm)->mappable_end;
 
 	if (mappable && fenceable)
-		vma->flags |= I915_VMA_CAN_FENCE;
+		set_bit(I915_VMA_CAN_FENCE_BIT, __i915_vma_flags(vma));
 	else
-		vma->flags &= ~I915_VMA_CAN_FENCE;
+		clear_bit(I915_VMA_CAN_FENCE_BIT, __i915_vma_flags(vma));
 }
 
 static bool color_differs(struct drm_mm_node *node, unsigned long color)
@@ -543,7 +544,7 @@ i915_vma_insert(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
 	int ret;
 
 	GEM_BUG_ON(i915_vma_is_closed(vma));
-	GEM_BUG_ON(vma->flags & (I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND));
+	GEM_BUG_ON(i915_vma_is_bound(vma, I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND));
 	GEM_BUG_ON(drm_mm_node_allocated(&vma->node));
 
 	size = max(size, vma->size);
@@ -677,7 +678,7 @@ static void
 i915_vma_remove(struct i915_vma *vma)
 {
 	GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
-	GEM_BUG_ON(vma->flags & (I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND));
+	GEM_BUG_ON(i915_vma_is_bound(vma, I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND));
 
 	vma->ops->clear_pages(vma);
 
@@ -708,7 +709,7 @@ i915_vma_remove(struct i915_vma *vma)
 int __i915_vma_do_pin(struct i915_vma *vma,
 		      u64 size, u64 alignment, u64 flags)
 {
-	const unsigned int bound = vma->flags;
+	const unsigned int bound = atomic_read(&vma->flags);
 	int ret;
 
 	lockdep_assert_held(&vma->vm->i915->drm.struct_mutex);
@@ -731,9 +732,9 @@ int __i915_vma_do_pin(struct i915_vma *vma,
 	if (ret)
 		goto err_remove;
 
-	GEM_BUG_ON((vma->flags & I915_VMA_BIND_MASK) == 0);
+	GEM_BUG_ON(!i915_vma_is_bound(vma, I915_VMA_BIND_MASK));
 
-	if ((bound ^ vma->flags) & I915_VMA_GLOBAL_BIND)
+	if ((bound ^ atomic_read(&vma->flags)) & I915_VMA_GLOBAL_BIND)
 		__i915_vma_set_map_and_fenceable(vma);
 
 	GEM_BUG_ON(i915_vma_misplaced(vma, size, alignment, flags));
@@ -743,7 +744,7 @@ int __i915_vma_do_pin(struct i915_vma *vma,
 	if ((bound & I915_VMA_BIND_MASK) == 0) {
 		i915_vma_remove(vma);
 		GEM_BUG_ON(vma->pages);
-		GEM_BUG_ON(vma->flags & I915_VMA_BIND_MASK);
+		GEM_BUG_ON(atomic_read(&vma->flags) & I915_VMA_BIND_MASK);
 	}
 err_unpin:
 	__i915_vma_unpin(vma);
@@ -986,7 +987,7 @@ int i915_vma_unbind(struct i915_vma *vma)
 		mutex_unlock(&vma->vm->mutex);
 
 		__i915_vma_iounmap(vma);
-		vma->flags &= ~I915_VMA_CAN_FENCE;
+		clear_bit(I915_VMA_CAN_FENCE_BIT, __i915_vma_flags(vma));
 	}
 	GEM_BUG_ON(vma->fence);
 	GEM_BUG_ON(i915_vma_has_userfault(vma));
@@ -995,7 +996,7 @@ int i915_vma_unbind(struct i915_vma *vma)
 		trace_i915_vma_unbind(vma);
 		vma->ops->unbind_vma(vma);
 	}
-	vma->flags &= ~(I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND);
+	atomic_and(~I915_VMA_BIND_MASK, &vma->flags);
 
 	i915_vma_remove(vma);
 
diff --git a/drivers/gpu/drm/i915/i915_vma.h b/drivers/gpu/drm/i915/i915_vma.h
index af2ef0a51455..6053ce1dc95c 100644
--- a/drivers/gpu/drm/i915/i915_vma.h
+++ b/drivers/gpu/drm/i915/i915_vma.h
@@ -72,7 +72,7 @@ struct i915_vma {
 	 * that exist in the ctx->handle_vmas LUT for this vma.
 	 */
 	atomic_t open_count;
-	unsigned long flags;
+	atomic_t flags;
 	/**
 	 * How many users have pinned this object in GTT space.
 	 *
@@ -97,18 +97,29 @@ struct i915_vma {
 	 * users.
 	 */
 #define I915_VMA_PIN_MASK 0xff
-#define I915_VMA_PIN_OVERFLOW	BIT(8)
+#define I915_VMA_PIN_OVERFLOW_BIT 8
+#define I915_VMA_PIN_OVERFLOW	((int)BIT(I915_VMA_PIN_OVERFLOW_BIT))
 
 	/** Flags and address space this VMA is bound to */
-#define I915_VMA_GLOBAL_BIND	BIT(9)
-#define I915_VMA_LOCAL_BIND	BIT(10)
-#define I915_VMA_BIND_MASK (I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND | I915_VMA_PIN_OVERFLOW)
+#define I915_VMA_GLOBAL_BIND_BIT 9
+#define I915_VMA_LOCAL_BIND_BIT 10
 
-#define I915_VMA_GGTT		BIT(11)
-#define I915_VMA_CAN_FENCE	BIT(12)
+#define I915_VMA_GLOBAL_BIND	((int)BIT(I915_VMA_GLOBAL_BIND_BIT))
+#define I915_VMA_LOCAL_BIND	((int)BIT(I915_VMA_LOCAL_BIND_BIT))
+
+#define I915_VMA_BIND_MASK (I915_VMA_GLOBAL_BIND | \
+			    I915_VMA_LOCAL_BIND | \
+			    I915_VMA_PIN_OVERFLOW)
+
+#define I915_VMA_GGTT_BIT	11
+#define I915_VMA_CAN_FENCE_BIT	12
 #define I915_VMA_USERFAULT_BIT	13
-#define I915_VMA_USERFAULT	BIT(I915_VMA_USERFAULT_BIT)
-#define I915_VMA_GGTT_WRITE	BIT(14)
+#define I915_VMA_GGTT_WRITE_BIT	14
+
+#define I915_VMA_GGTT		((int)BIT(I915_VMA_GGTT_BIT))
+#define I915_VMA_CAN_FENCE	((int)BIT(I915_VMA_CAN_FENCE_BIT))
+#define I915_VMA_USERFAULT	((int)BIT(I915_VMA_USERFAULT_BIT))
+#define I915_VMA_GGTT_WRITE	((int)BIT(I915_VMA_GGTT_WRITE_BIT))
 
 	struct i915_active active;
 
@@ -162,48 +173,51 @@ int __must_check i915_vma_move_to_active(struct i915_vma *vma,
 					 struct i915_request *rq,
 					 unsigned int flags);
 
+#define __i915_vma_flags(v) ((unsigned long *)&(v)->flags.counter)
+
 static inline bool i915_vma_is_ggtt(const struct i915_vma *vma)
 {
-	return vma->flags & I915_VMA_GGTT;
+	return test_bit(I915_VMA_GGTT_BIT, __i915_vma_flags(vma));
 }
 
 static inline bool i915_vma_has_ggtt_write(const struct i915_vma *vma)
 {
-	return vma->flags & I915_VMA_GGTT_WRITE;
+	return test_bit(I915_VMA_GGTT_WRITE_BIT, __i915_vma_flags(vma));
 }
 
 static inline void i915_vma_set_ggtt_write(struct i915_vma *vma)
 {
 	GEM_BUG_ON(!i915_vma_is_ggtt(vma));
-	vma->flags |= I915_VMA_GGTT_WRITE;
+	set_bit(I915_VMA_GGTT_WRITE_BIT, __i915_vma_flags(vma));
 }
 
-static inline void i915_vma_unset_ggtt_write(struct i915_vma *vma)
+static inline bool i915_vma_unset_ggtt_write(struct i915_vma *vma)
 {
-	vma->flags &= ~I915_VMA_GGTT_WRITE;
+	return test_and_clear_bit(I915_VMA_GGTT_WRITE_BIT,
+				  __i915_vma_flags(vma));
 }
 
 void i915_vma_flush_writes(struct i915_vma *vma);
 
 static inline bool i915_vma_is_map_and_fenceable(const struct i915_vma *vma)
 {
-	return vma->flags & I915_VMA_CAN_FENCE;
+	return test_bit(I915_VMA_CAN_FENCE_BIT, __i915_vma_flags(vma));
 }
 
 static inline bool i915_vma_set_userfault(struct i915_vma *vma)
 {
 	GEM_BUG_ON(!i915_vma_is_map_and_fenceable(vma));
-	return __test_and_set_bit(I915_VMA_USERFAULT_BIT, &vma->flags);
+	return test_and_set_bit(I915_VMA_USERFAULT_BIT, __i915_vma_flags(vma));
 }
 
 static inline void i915_vma_unset_userfault(struct i915_vma *vma)
 {
-	return __clear_bit(I915_VMA_USERFAULT_BIT, &vma->flags);
+	return clear_bit(I915_VMA_USERFAULT_BIT, __i915_vma_flags(vma));
 }
 
 static inline bool i915_vma_has_userfault(const struct i915_vma *vma)
 {
-	return test_bit(I915_VMA_USERFAULT_BIT, &vma->flags);
+	return test_bit(I915_VMA_USERFAULT_BIT, __i915_vma_flags(vma));
 }
 
 static inline bool i915_vma_is_closed(const struct i915_vma *vma)
@@ -330,7 +344,7 @@ i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
 	/* Pin early to prevent the shrinker/eviction logic from destroying
 	 * our vma as we insert and bind.
 	 */
-	if (likely(((++vma->flags ^ flags) & I915_VMA_BIND_MASK) == 0)) {
+	if (likely(((atomic_inc_return(&vma->flags) ^ flags) & I915_VMA_BIND_MASK) == 0)) {
 		GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
 		GEM_BUG_ON(i915_vma_misplaced(vma, size, alignment, flags));
 		return 0;
@@ -341,7 +355,7 @@ i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
 
 static inline int i915_vma_pin_count(const struct i915_vma *vma)
 {
-	return vma->flags & I915_VMA_PIN_MASK;
+	return atomic_read(&vma->flags) & I915_VMA_PIN_MASK;
 }
 
 static inline bool i915_vma_is_pinned(const struct i915_vma *vma)
@@ -351,13 +365,13 @@ static inline bool i915_vma_is_pinned(const struct i915_vma *vma)
 
 static inline void __i915_vma_pin(struct i915_vma *vma)
 {
-	vma->flags++;
-	GEM_BUG_ON(vma->flags & I915_VMA_PIN_OVERFLOW);
+	atomic_inc(&vma->flags);
+	GEM_BUG_ON(atomic_read(&vma->flags) & I915_VMA_PIN_OVERFLOW);
 }
 
 static inline void __i915_vma_unpin(struct i915_vma *vma)
 {
-	vma->flags--;
+	atomic_dec(&vma->flags);
 }
 
 static inline void i915_vma_unpin(struct i915_vma *vma)
@@ -370,7 +384,7 @@ static inline void i915_vma_unpin(struct i915_vma *vma)
 static inline bool i915_vma_is_bound(const struct i915_vma *vma,
 				     unsigned int where)
 {
-	return vma->flags & where;
+	return atomic_read(&vma->flags) & where;
 }
 
 /**
diff --git a/drivers/gpu/drm/i915/selftests/mock_gtt.c b/drivers/gpu/drm/i915/selftests/mock_gtt.c
index e62a67e0f79c..366335981086 100644
--- a/drivers/gpu/drm/i915/selftests/mock_gtt.c
+++ b/drivers/gpu/drm/i915/selftests/mock_gtt.c
@@ -43,7 +43,7 @@ static int mock_bind_ppgtt(struct i915_vma *vma,
 			   u32 flags)
 {
 	GEM_BUG_ON(flags & I915_VMA_GLOBAL_BIND);
-	vma->flags |= I915_VMA_LOCAL_BIND;
+	set_bit(I915_VMA_LOCAL_BIND_BIT, __i915_vma_flags(vma));
 	return 0;
 }
 
@@ -86,7 +86,7 @@ static int mock_bind_ggtt(struct i915_vma *vma,
 			  enum i915_cache_level cache_level,
 			  u32 flags)
 {
-	vma->flags |= I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND;
+	atomic_or(I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND, &vma->flags);
 	return 0;
 }
 
-- 
2.23.0

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

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

* [PATCH 11/21] drm/i915/gtt: Make sure the gen6 ppgtt is bound before first use
  2019-09-02  4:02 [PATCH 01/21] drm/i915: Restrict the aliasing-ppgtt to the size of the ggtt Chris Wilson
                   ` (8 preceding siblings ...)
  2019-09-02  4:02 ` [PATCH 10/21] drm/i915: Make i915_vma.flags atomic_t for mutex reduction Chris Wilson
@ 2019-09-02  4:02 ` Chris Wilson
  2019-09-10 20:17   ` Matthew Auld
  2019-09-02  4:02 ` [PATCH 12/21] drm/i915: Mark up address spaces that may need to allocate Chris Wilson
                   ` (13 subsequent siblings)
  23 siblings, 1 reply; 53+ messages in thread
From: Chris Wilson @ 2019-09-02  4:02 UTC (permalink / raw)
  To: intel-gfx

As we remove the struct_mutex protection from around the vma pinning,
counters need to be atomic and aware that there may be multiple threads
simultaneously active.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 drivers/gpu/drm/i915/i915_gem_gtt.c | 39 ++++++++++++++++-------------
 drivers/gpu/drm/i915/i915_gem_gtt.h |  4 ++-
 2 files changed, 24 insertions(+), 19 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
index d5bf57fa837b..9095f017162e 100644
--- a/drivers/gpu/drm/i915/i915_gem_gtt.c
+++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
@@ -1777,6 +1777,8 @@ static void gen6_ppgtt_cleanup(struct i915_address_space *vm)
 
 	gen6_ppgtt_free_pd(ppgtt);
 	free_scratch(vm);
+
+	mutex_destroy(&ppgtt->pin_mutex);
 	kfree(ppgtt->base.pd);
 }
 
@@ -1878,7 +1880,7 @@ static struct i915_vma *pd_vma_create(struct gen6_ppgtt *ppgtt, int size)
 int gen6_ppgtt_pin(struct i915_ppgtt *base)
 {
 	struct gen6_ppgtt *ppgtt = to_gen6_ppgtt(base);
-	int err;
+	int err = 0;
 
 	GEM_BUG_ON(ppgtt->base.vm.closed);
 
@@ -1888,24 +1890,26 @@ int gen6_ppgtt_pin(struct i915_ppgtt *base)
 	 * (When vma->pin_count becomes atomic, I expect we will naturally
 	 * need a larger, unpacked, type and kill this redundancy.)
 	 */
-	if (ppgtt->pin_count++)
+	if (atomic_add_unless(&ppgtt->pin_count, 1, 0))
 		return 0;
 
+	if (mutex_lock_interruptible(&ppgtt->pin_mutex))
+		return -EINTR;
+
 	/*
 	 * PPGTT PDEs reside in the GGTT and consists of 512 entries. The
 	 * allocator works in address space sizes, so it's multiplied by page
 	 * size. We allocate at the top of the GTT to avoid fragmentation.
 	 */
-	err = i915_vma_pin(ppgtt->vma,
-			   0, GEN6_PD_ALIGN,
-			   PIN_GLOBAL | PIN_HIGH);
-	if (err)
-		goto unpin;
-
-	return 0;
+	if (!atomic_read(&ppgtt->pin_count)) {
+		err = i915_vma_pin(ppgtt->vma,
+				   0, GEN6_PD_ALIGN,
+				   PIN_GLOBAL | PIN_HIGH);
+	}
+	if (!err)
+		atomic_inc(&ppgtt->pin_count);
+	mutex_unlock(&ppgtt->pin_mutex);
 
-unpin:
-	ppgtt->pin_count = 0;
 	return err;
 }
 
@@ -1913,22 +1917,19 @@ void gen6_ppgtt_unpin(struct i915_ppgtt *base)
 {
 	struct gen6_ppgtt *ppgtt = to_gen6_ppgtt(base);
 
-	GEM_BUG_ON(!ppgtt->pin_count);
-	if (--ppgtt->pin_count)
-		return;
-
-	i915_vma_unpin(ppgtt->vma);
+	GEM_BUG_ON(!atomic_read(&ppgtt->pin_count));
+	atomic_dec(&ppgtt->pin_count);
 }
 
 void gen6_ppgtt_unpin_all(struct i915_ppgtt *base)
 {
 	struct gen6_ppgtt *ppgtt = to_gen6_ppgtt(base);
 
-	if (!ppgtt->pin_count)
+	if (!atomic_read(&ppgtt->pin_count))
 		return;
 
-	ppgtt->pin_count = 0;
 	i915_vma_unpin(ppgtt->vma);
+	atomic_set(&ppgtt->pin_count, 0);
 }
 
 static struct i915_ppgtt *gen6_ppgtt_create(struct drm_i915_private *i915)
@@ -1941,6 +1942,8 @@ static struct i915_ppgtt *gen6_ppgtt_create(struct drm_i915_private *i915)
 	if (!ppgtt)
 		return ERR_PTR(-ENOMEM);
 
+	mutex_init(&ppgtt->pin_mutex);
+
 	ppgtt_init(&ppgtt->base, &i915->gt);
 	ppgtt->base.vm.top = 1;
 
diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.h b/drivers/gpu/drm/i915/i915_gem_gtt.h
index 546a7c0aa0e7..57d27898639a 100644
--- a/drivers/gpu/drm/i915/i915_gem_gtt.h
+++ b/drivers/gpu/drm/i915/i915_gem_gtt.h
@@ -426,7 +426,9 @@ struct gen6_ppgtt {
 	struct i915_vma *vma;
 	gen6_pte_t __iomem *pd_addr;
 
-	unsigned int pin_count;
+	atomic_t pin_count;
+	struct mutex pin_mutex;
+
 	bool scan_for_unused_pt;
 };
 
-- 
2.23.0

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

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

* [PATCH 12/21] drm/i915: Mark up address spaces that may need to allocate
  2019-09-02  4:02 [PATCH 01/21] drm/i915: Restrict the aliasing-ppgtt to the size of the ggtt Chris Wilson
                   ` (9 preceding siblings ...)
  2019-09-02  4:02 ` [PATCH 11/21] drm/i915/gtt: Make sure the gen6 ppgtt is bound before first use Chris Wilson
@ 2019-09-02  4:02 ` Chris Wilson
  2019-09-20 16:22   ` Tvrtko Ursulin
  2019-09-02  4:02 ` [PATCH 13/21] drm/i915: Pull i915_vma_pin under the vm->mutex Chris Wilson
                   ` (12 subsequent siblings)
  23 siblings, 1 reply; 53+ messages in thread
From: Chris Wilson @ 2019-09-02  4:02 UTC (permalink / raw)
  To: intel-gfx

Since we cannot allocate underneath the vm->mutex (it is used in the
direct-reclaim paths), we need to shift the allocations off into a
mutexless worker with fence recursion prevention. To know when we need
this protection, we mark up the address spaces that do allocate before
insertion.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 drivers/gpu/drm/i915/i915_gem_gtt.c | 3 +++
 drivers/gpu/drm/i915/i915_gem_gtt.h | 2 ++
 2 files changed, 5 insertions(+)

diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
index 9095f017162e..56d27cf09a3d 100644
--- a/drivers/gpu/drm/i915/i915_gem_gtt.c
+++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
@@ -1500,6 +1500,7 @@ static struct i915_ppgtt *gen8_ppgtt_create(struct drm_i915_private *i915)
 			goto err_free_pd;
 	}
 
+	ppgtt->vm.bind_alloc = 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;
@@ -1947,6 +1948,7 @@ static struct i915_ppgtt *gen6_ppgtt_create(struct drm_i915_private *i915)
 	ppgtt_init(&ppgtt->base, &i915->gt);
 	ppgtt->base.vm.top = 1;
 
+	ppgtt->base.vm.bind_alloc = 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;
@@ -2578,6 +2580,7 @@ static int init_aliasing_ppgtt(struct i915_ggtt *ggtt)
 		goto err_ppgtt;
 
 	ggtt->alias = ppgtt;
+	ggtt->vm.bind_alloc |= ppgtt->vm.bind_alloc;
 
 	GEM_BUG_ON(ggtt->vm.vma_ops.bind_vma != ggtt_bind_vma);
 	ggtt->vm.vma_ops.bind_vma = aliasing_gtt_bind_vma;
diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.h b/drivers/gpu/drm/i915/i915_gem_gtt.h
index 57d27898639a..007bdaf4ba00 100644
--- a/drivers/gpu/drm/i915/i915_gem_gtt.h
+++ b/drivers/gpu/drm/i915/i915_gem_gtt.h
@@ -305,6 +305,8 @@ struct i915_address_space {
 	u64 total;		/* size addr space maps (ex. 2GB for ggtt) */
 	u64 reserved;		/* size addr space reserved */
 
+	unsigned int bind_alloc;
+
 	bool closed;
 
 	struct mutex mutex; /* protects vma and our lists */
-- 
2.23.0

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

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

* [PATCH 13/21] drm/i915: Pull i915_vma_pin under the vm->mutex
  2019-09-02  4:02 [PATCH 01/21] drm/i915: Restrict the aliasing-ppgtt to the size of the ggtt Chris Wilson
                   ` (10 preceding siblings ...)
  2019-09-02  4:02 ` [PATCH 12/21] drm/i915: Mark up address spaces that may need to allocate Chris Wilson
@ 2019-09-02  4:02 ` Chris Wilson
  2019-09-16 10:13   ` Tvrtko Ursulin
  2019-09-17 12:37   ` Tvrtko Ursulin
  2019-09-02  4:02 ` [PATCH 14/21] drm/i915: Push the i915_active.retire into a worker Chris Wilson
                   ` (11 subsequent siblings)
  23 siblings, 2 replies; 53+ messages in thread
From: Chris Wilson @ 2019-09-02  4:02 UTC (permalink / raw)
  To: intel-gfx

Replace the struct_mutex requirement for pinning the i915_vma with the
local vm->mutex instead. Note that the vm->mutex is tainted by the
shrinker (we require unbinding from inside fs-reclaim) and so we cannot
allocate while holding that mutex. Instead we have to preallocate
workers to do allocate and apply the PTE updates after we have we
reserved their slot in the drm_mm (using fences to order the PTE writes
with the GPU work and with later unbind).

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 drivers/gpu/drm/i915/display/intel_display.c  |  29 +-
 drivers/gpu/drm/i915/display/intel_fbdev.c    |   8 +-
 drivers/gpu/drm/i915/display/intel_overlay.c  |  11 +-
 .../gpu/drm/i915/gem/i915_gem_client_blt.c    |  13 +-
 drivers/gpu/drm/i915/gem/i915_gem_context.c   |  20 +-
 drivers/gpu/drm/i915/gem/i915_gem_domain.c    |  19 +-
 .../gpu/drm/i915/gem/i915_gem_execbuffer.c    |  48 +-
 drivers/gpu/drm/i915/gem/i915_gem_mman.c      |  24 +-
 drivers/gpu/drm/i915/gem/i915_gem_object.c    |  33 +-
 drivers/gpu/drm/i915/gem/i915_gem_object.h    |   5 +
 drivers/gpu/drm/i915/gem/i915_gem_shrinker.c  |  71 +--
 drivers/gpu/drm/i915/gem/i915_gem_stolen.c    |   8 +-
 drivers/gpu/drm/i915/gem/i915_gem_tiling.c    |  15 +-
 drivers/gpu/drm/i915/gem/i915_gem_userptr.c   |  27 +-
 .../gpu/drm/i915/gem/selftests/huge_pages.c   |  23 +-
 .../drm/i915/gem/selftests/i915_gem_context.c |  12 +-
 .../drm/i915/gem/selftests/i915_gem_mman.c    |   2 -
 .../drm/i915/gem/selftests/igt_gem_utils.c    |   7 +-
 drivers/gpu/drm/i915/gt/intel_gt.c            |   5 +-
 drivers/gpu/drm/i915/gt/intel_ringbuffer.c    |   4 +-
 drivers/gpu/drm/i915/gt/selftest_hangcheck.c  |  19 +-
 drivers/gpu/drm/i915/gvt/aperture_gm.c        |  12 +-
 drivers/gpu/drm/i915/i915_active.c            |  94 +++-
 drivers/gpu/drm/i915/i915_active.h            |   7 +
 drivers/gpu/drm/i915/i915_active_types.h      |   5 +
 drivers/gpu/drm/i915/i915_gem.c               |  94 ++--
 drivers/gpu/drm/i915/i915_gem_evict.c         |  20 +-
 drivers/gpu/drm/i915/i915_gem_fence_reg.c     |   5 +-
 drivers/gpu/drm/i915/i915_gem_gtt.c           | 104 ++--
 drivers/gpu/drm/i915/i915_gem_gtt.h           |  38 +-
 drivers/gpu/drm/i915/i915_perf.c              |  32 +-
 drivers/gpu/drm/i915/i915_vma.c               | 476 ++++++++++++------
 drivers/gpu/drm/i915/i915_vma.h               |  75 ++-
 .../gpu/drm/i915/selftests/i915_gem_evict.c   |  36 +-
 drivers/gpu/drm/i915/selftests/i915_gem_gtt.c |  91 ++--
 drivers/gpu/drm/i915/selftests/i915_vma.c     |   8 +-
 36 files changed, 818 insertions(+), 682 deletions(-)

diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c
index 5e3b22e3f61d..81c0d7a35c4c 100644
--- a/drivers/gpu/drm/i915/display/intel_display.c
+++ b/drivers/gpu/drm/i915/display/intel_display.c
@@ -2079,7 +2079,6 @@ intel_pin_and_fence_fb_obj(struct drm_framebuffer *fb,
 	unsigned int pinctl;
 	u32 alignment;
 
-	WARN_ON(!mutex_is_locked(&dev->struct_mutex));
 	if (WARN_ON(!i915_gem_object_is_framebuffer(obj)))
 		return ERR_PTR(-EINVAL);
 
@@ -2163,8 +2162,6 @@ intel_pin_and_fence_fb_obj(struct drm_framebuffer *fb,
 
 void intel_unpin_fb_vma(struct i915_vma *vma, unsigned long flags)
 {
-	lockdep_assert_held(&vma->vm->i915->drm.struct_mutex);
-
 	i915_gem_object_lock(vma->obj);
 	if (flags & PLANE_HAS_FENCE)
 		i915_vma_unpin_fence(vma);
@@ -3065,12 +3062,10 @@ intel_alloc_initial_plane_obj(struct intel_crtc *crtc,
 		return false;
 	}
 
-	mutex_lock(&dev->struct_mutex);
 	obj = i915_gem_object_create_stolen_for_preallocated(dev_priv,
 							     base_aligned,
 							     base_aligned,
 							     size_aligned);
-	mutex_unlock(&dev->struct_mutex);
 	if (!obj)
 		return false;
 
@@ -3232,13 +3227,11 @@ intel_find_initial_plane_obj(struct intel_crtc *intel_crtc,
 	intel_state->color_plane[0].stride =
 		intel_fb_pitch(fb, 0, intel_state->base.rotation);
 
-	mutex_lock(&dev->struct_mutex);
 	intel_state->vma =
 		intel_pin_and_fence_fb_obj(fb,
 					   &intel_state->view,
 					   intel_plane_uses_fence(intel_state),
 					   &intel_state->flags);
-	mutex_unlock(&dev->struct_mutex);
 	if (IS_ERR(intel_state->vma)) {
 		DRM_ERROR("failed to pin boot fb on pipe %d: %li\n",
 			  intel_crtc->pipe, PTR_ERR(intel_state->vma));
@@ -14364,8 +14357,6 @@ static void fb_obj_bump_render_priority(struct drm_i915_gem_object *obj)
  * bits.  Some older platforms need special physical address handling for
  * cursor planes.
  *
- * Must be called with struct_mutex held.
- *
  * Returns 0 on success, negative error code on failure.
  */
 int
@@ -14422,15 +14413,8 @@ intel_prepare_plane_fb(struct drm_plane *plane,
 	if (ret)
 		return ret;
 
-	ret = mutex_lock_interruptible(&dev_priv->drm.struct_mutex);
-	if (ret) {
-		i915_gem_object_unpin_pages(obj);
-		return ret;
-	}
-
 	ret = intel_plane_pin_fb(to_intel_plane_state(new_state));
 
-	mutex_unlock(&dev_priv->drm.struct_mutex);
 	i915_gem_object_unpin_pages(obj);
 	if (ret)
 		return ret;
@@ -14479,8 +14463,6 @@ intel_prepare_plane_fb(struct drm_plane *plane,
  * @old_state: the state from the previous modeset
  *
  * Cleans up a framebuffer that has just been removed from a plane.
- *
- * Must be called with struct_mutex held.
  */
 void
 intel_cleanup_plane_fb(struct drm_plane *plane,
@@ -14496,9 +14478,7 @@ intel_cleanup_plane_fb(struct drm_plane *plane,
 	}
 
 	/* Should only be called after a successful intel_prepare_plane_fb()! */
-	mutex_lock(&dev_priv->drm.struct_mutex);
 	intel_plane_unpin_fb(to_intel_plane_state(old_state));
-	mutex_unlock(&dev_priv->drm.struct_mutex);
 }
 
 int
@@ -14698,7 +14678,6 @@ intel_legacy_cursor_update(struct drm_plane *plane,
 			   u32 src_w, u32 src_h,
 			   struct drm_modeset_acquire_ctx *ctx)
 {
-	struct drm_i915_private *dev_priv = to_i915(crtc->dev);
 	struct drm_plane_state *old_plane_state, *new_plane_state;
 	struct intel_plane *intel_plane = to_intel_plane(plane);
 	struct intel_crtc_state *crtc_state =
@@ -14764,13 +14743,9 @@ intel_legacy_cursor_update(struct drm_plane *plane,
 	if (ret)
 		goto out_free;
 
-	ret = mutex_lock_interruptible(&dev_priv->drm.struct_mutex);
-	if (ret)
-		goto out_free;
-
 	ret = intel_plane_pin_fb(to_intel_plane_state(new_plane_state));
 	if (ret)
-		goto out_unlock;
+		goto out_free;
 
 	intel_frontbuffer_flush(to_intel_frontbuffer(fb), ORIGIN_FLIP);
 	intel_frontbuffer_track(to_intel_frontbuffer(old_plane_state->fb),
@@ -14800,8 +14775,6 @@ intel_legacy_cursor_update(struct drm_plane *plane,
 
 	intel_plane_unpin_fb(to_intel_plane_state(old_plane_state));
 
-out_unlock:
-	mutex_unlock(&dev_priv->drm.struct_mutex);
 out_free:
 	if (new_crtc_state)
 		intel_crtc_destroy_state(crtc, &new_crtc_state->base);
diff --git a/drivers/gpu/drm/i915/display/intel_fbdev.c b/drivers/gpu/drm/i915/display/intel_fbdev.c
index d59eee5c5d9c..a3dea3f2dacd 100644
--- a/drivers/gpu/drm/i915/display/intel_fbdev.c
+++ b/drivers/gpu/drm/i915/display/intel_fbdev.c
@@ -204,7 +204,6 @@ static int intelfb_create(struct drm_fb_helper *helper,
 		sizes->fb_height = intel_fb->base.height;
 	}
 
-	mutex_lock(&dev->struct_mutex);
 	wakeref = intel_runtime_pm_get(&dev_priv->runtime_pm);
 
 	/* Pin the GGTT vma for our access via info->screen_base.
@@ -266,7 +265,6 @@ static int intelfb_create(struct drm_fb_helper *helper,
 	ifbdev->vma_flags = flags;
 
 	intel_runtime_pm_put(&dev_priv->runtime_pm, wakeref);
-	mutex_unlock(&dev->struct_mutex);
 	vga_switcheroo_client_fb_set(pdev, info);
 	return 0;
 
@@ -274,7 +272,6 @@ static int intelfb_create(struct drm_fb_helper *helper,
 	intel_unpin_fb_vma(vma, flags);
 out_unlock:
 	intel_runtime_pm_put(&dev_priv->runtime_pm, wakeref);
-	mutex_unlock(&dev->struct_mutex);
 	return ret;
 }
 
@@ -291,11 +288,8 @@ static void intel_fbdev_destroy(struct intel_fbdev *ifbdev)
 
 	drm_fb_helper_fini(&ifbdev->helper);
 
-	if (ifbdev->vma) {
-		mutex_lock(&ifbdev->helper.dev->struct_mutex);
+	if (ifbdev->vma)
 		intel_unpin_fb_vma(ifbdev->vma, ifbdev->vma_flags);
-		mutex_unlock(&ifbdev->helper.dev->struct_mutex);
-	}
 
 	if (ifbdev->fb)
 		drm_framebuffer_remove(&ifbdev->fb->base);
diff --git a/drivers/gpu/drm/i915/display/intel_overlay.c b/drivers/gpu/drm/i915/display/intel_overlay.c
index 29edfc343716..4f36557b3f3b 100644
--- a/drivers/gpu/drm/i915/display/intel_overlay.c
+++ b/drivers/gpu/drm/i915/display/intel_overlay.c
@@ -1303,15 +1303,11 @@ static int get_registers(struct intel_overlay *overlay, bool use_phys)
 	struct i915_vma *vma;
 	int err;
 
-	mutex_lock(&i915->drm.struct_mutex);
-
 	obj = i915_gem_object_create_stolen(i915, PAGE_SIZE);
 	if (obj == NULL)
 		obj = i915_gem_object_create_internal(i915, PAGE_SIZE);
-	if (IS_ERR(obj)) {
-		err = PTR_ERR(obj);
-		goto err_unlock;
-	}
+	if (IS_ERR(obj))
+		return PTR_ERR(obj);
 
 	vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0, PIN_MAPPABLE);
 	if (IS_ERR(vma)) {
@@ -1332,13 +1328,10 @@ static int get_registers(struct intel_overlay *overlay, bool use_phys)
 	}
 
 	overlay->reg_bo = obj;
-	mutex_unlock(&i915->drm.struct_mutex);
 	return 0;
 
 err_put_bo:
 	i915_gem_object_put(obj);
-err_unlock:
-	mutex_unlock(&i915->drm.struct_mutex);
 	return err;
 }
 
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 f99920652751..9e72b42a86f5 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_client_blt.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_client_blt.c
@@ -152,6 +152,17 @@ static void clear_pages_dma_fence_cb(struct dma_fence *fence,
 	irq_work_queue(&w->irq_work);
 }
 
+static int move_to_active(struct i915_vma *vma, struct i915_request *rq)
+{
+	int err;
+
+	err = i915_request_await_active(rq, &vma->active);
+	if (err)
+		return err;
+
+	return i915_active_ref(&vma->active, rq->timeline, rq);
+}
+
 static void clear_pages_worker(struct work_struct *work)
 {
 	struct clear_pages_work *w = container_of(work, typeof(*w), work);
@@ -211,7 +222,7 @@ static void clear_pages_worker(struct work_struct *work)
 	 * keep track of the GPU activity within this vma/request, and
 	 * propagate the signal from the request to w->dma.
 	 */
-	err = i915_active_ref(&vma->active, rq->timeline, rq);
+	err = move_to_active(vma, rq);
 	if (err)
 		goto out_request;
 
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context.c b/drivers/gpu/drm/i915/gem/i915_gem_context.c
index f1c0e5d958f3..653f7275306a 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_context.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_context.c
@@ -313,8 +313,6 @@ static void i915_gem_context_free(struct i915_gem_context *ctx)
 	GEM_BUG_ON(!i915_gem_context_is_closed(ctx));
 
 	release_hw_id(ctx);
-	if (ctx->vm)
-		i915_vm_put(ctx->vm);
 
 	free_engines(rcu_access_pointer(ctx->engines));
 	mutex_destroy(&ctx->engines_mutex);
@@ -379,9 +377,13 @@ void i915_gem_context_release(struct kref *ref)
 
 static void context_close(struct i915_gem_context *ctx)
 {
+	i915_gem_context_set_closed(ctx);
+
+	if (ctx->vm)
+		i915_vm_close(ctx->vm);
+
 	mutex_lock(&ctx->mutex);
 
-	i915_gem_context_set_closed(ctx);
 	ctx->file_priv = ERR_PTR(-EBADF);
 
 	/*
@@ -474,7 +476,7 @@ __set_ppgtt(struct i915_gem_context *ctx, struct i915_address_space *vm)
 
 	GEM_BUG_ON(old && i915_vm_is_4lvl(vm) != i915_vm_is_4lvl(old));
 
-	ctx->vm = i915_vm_get(vm);
+	ctx->vm = i915_vm_open(vm);
 	context_apply_all(ctx, __apply_ppgtt, vm);
 
 	return old;
@@ -488,7 +490,7 @@ static void __assign_ppgtt(struct i915_gem_context *ctx,
 
 	vm = __set_ppgtt(ctx, vm);
 	if (vm)
-		i915_vm_put(vm);
+		i915_vm_close(vm);
 }
 
 static void __set_timeline(struct intel_timeline **dst,
@@ -953,7 +955,7 @@ static int get_ppgtt(struct drm_i915_file_private *file_priv,
 	if (ret < 0)
 		goto err_unlock;
 
-	i915_vm_get(vm);
+	i915_vm_open(vm);
 
 	args->size = 0;
 	args->value = ret;
@@ -973,7 +975,7 @@ static void set_ppgtt_barrier(void *data)
 	if (INTEL_GEN(old->i915) < 8)
 		gen6_ppgtt_unpin_all(i915_vm_to_ppgtt(old));
 
-	i915_vm_put(old);
+	i915_vm_close(old);
 }
 
 static int emit_ppgtt_update(struct i915_request *rq, void *data)
@@ -1090,8 +1092,8 @@ static int set_ppgtt(struct drm_i915_file_private *file_priv,
 				   set_ppgtt_barrier,
 				   old);
 	if (err) {
-		i915_vm_put(__set_ppgtt(ctx, old));
-		i915_vm_put(old);
+		i915_vm_close(__set_ppgtt(ctx, old));
+		i915_vm_close(old);
 	}
 
 unlock:
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_domain.c b/drivers/gpu/drm/i915/gem/i915_gem_domain.c
index f0c437b6e995..46a23409e1c0 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_domain.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_domain.c
@@ -202,7 +202,11 @@ int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj,
 		    i915_gem_valid_gtt_space(vma, cache_level))
 			continue;
 
-		ret = i915_vma_unbind(vma);
+		ret = mutex_lock_interruptible(&vma->vm->mutex);
+		if (!ret) {
+			ret = i915_vma_unbind(vma);
+			mutex_unlock(&vma->vm->mutex);
+		}
 		if (ret)
 			return ret;
 
@@ -288,7 +292,12 @@ int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj,
 			if (!drm_mm_node_allocated(&vma->node))
 				continue;
 
-			ret = i915_vma_bind(vma, cache_level, PIN_UPDATE);
+			/* Wait for an earlier async bind */
+			ret = i915_active_wait(&vma->active);
+			if (ret)
+				return ret;
+
+			ret = i915_vma_bind(vma, cache_level, PIN_UPDATE, NULL);
 			if (ret)
 				return ret;
 		}
@@ -389,16 +398,11 @@ int i915_gem_set_caching_ioctl(struct drm_device *dev, void *data,
 	if (ret)
 		goto out;
 
-	ret = mutex_lock_interruptible(&i915->drm.struct_mutex);
-	if (ret)
-		goto out;
-
 	ret = i915_gem_object_lock_interruptible(obj);
 	if (ret == 0) {
 		ret = i915_gem_object_set_cache_level(obj, level);
 		i915_gem_object_unlock(obj);
 	}
-	mutex_unlock(&i915->drm.struct_mutex);
 
 out:
 	i915_gem_object_put(obj);
@@ -483,6 +487,7 @@ static void i915_gem_object_bump_inactive_ggtt(struct drm_i915_gem_object *obj)
 		if (!drm_mm_node_allocated(&vma->node))
 			continue;
 
+		GEM_BUG_ON(vma->vm != &i915->ggtt.vm);
 		list_move_tail(&vma->vm_link, &vma->vm->bound_list);
 	}
 	mutex_unlock(&i915->ggtt.vm.mutex);
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
index c049199a1df5..c3dd8ce7e2b7 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
@@ -550,8 +550,11 @@ eb_add_vma(struct i915_execbuffer *eb,
 		eb_unreserve_vma(vma, vma->exec_flags);
 
 		list_add_tail(&vma->exec_link, &eb->unbound);
-		if (drm_mm_node_allocated(&vma->node))
+		if (drm_mm_node_allocated(&vma->node)) {
+			mutex_lock(&vma->vm->mutex);
 			err = i915_vma_unbind(vma);
+			mutex_unlock(&vma->vm->mutex);
+		}
 		if (unlikely(err))
 			vma->exec_flags = NULL;
 	}
@@ -698,7 +701,9 @@ static int eb_reserve(struct i915_execbuffer *eb)
 
 		case 1:
 			/* Too fragmented, unbind everything and retry */
+			mutex_lock(&eb->context->vm->mutex);
 			err = i915_gem_evict_vm(eb->context->vm);
+			mutex_unlock(&eb->context->vm->mutex);
 			if (err)
 				return err;
 			break;
@@ -972,7 +977,9 @@ static void reloc_cache_reset(struct reloc_cache *cache)
 			ggtt->vm.clear_range(&ggtt->vm,
 					     cache->node.start,
 					     cache->node.size);
+			mutex_lock(&ggtt->vm.mutex);
 			drm_mm_remove_node(&cache->node);
+			mutex_unlock(&ggtt->vm.mutex);
 		} else {
 			i915_vma_unpin((struct i915_vma *)cache->node.mm);
 		}
@@ -1047,11 +1054,13 @@ static void *reloc_iomap(struct drm_i915_gem_object *obj,
 					       PIN_NOEVICT);
 		if (IS_ERR(vma)) {
 			memset(&cache->node, 0, sizeof(cache->node));
+			mutex_lock(&ggtt->vm.mutex);
 			err = drm_mm_insert_node_in_range
 				(&ggtt->vm.mm, &cache->node,
 				 PAGE_SIZE, 0, I915_COLOR_UNEVICTABLE,
 				 0, ggtt->mappable_end,
 				 DRM_MM_INSERT_LOW);
+			mutex_unlock(&ggtt->vm.mutex);
 			if (err) /* no inactive aperture space, use cpu reloc */
 				return NULL;
 		} else {
@@ -1416,7 +1425,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, target->obj->cache_level,
-					    PIN_GLOBAL);
+					    PIN_GLOBAL, NULL);
 			if (WARN_ONCE(err,
 				      "Unexpected failure to bind target VMA!"))
 				return err;
@@ -2140,35 +2149,6 @@ static struct i915_request *eb_throttle(struct intel_context *ce)
 	return i915_request_get(rq);
 }
 
-static int
-__eb_pin_context(struct i915_execbuffer *eb, struct intel_context *ce)
-{
-	int err;
-
-	if (likely(atomic_inc_not_zero(&ce->pin_count)))
-		return 0;
-
-	err = mutex_lock_interruptible(&eb->i915->drm.struct_mutex);
-	if (err)
-		return err;
-
-	err = __intel_context_do_pin(ce);
-	mutex_unlock(&eb->i915->drm.struct_mutex);
-
-	return err;
-}
-
-static void
-__eb_unpin_context(struct i915_execbuffer *eb, struct intel_context *ce)
-{
-	if (likely(atomic_add_unless(&ce->pin_count, -1, 1)))
-		return;
-
-	mutex_lock(&eb->i915->drm.struct_mutex);
-	intel_context_unpin(ce);
-	mutex_unlock(&eb->i915->drm.struct_mutex);
-}
-
 static int __eb_pin_engine(struct i915_execbuffer *eb, struct intel_context *ce)
 {
 	struct intel_timeline *tl;
@@ -2188,7 +2168,7 @@ static int __eb_pin_engine(struct i915_execbuffer *eb, struct intel_context *ce)
 	 * GGTT space, so do this first before we reserve a seqno for
 	 * ourselves.
 	 */
-	err = __eb_pin_context(eb, ce);
+	err = intel_context_pin(ce);
 	if (err)
 		return err;
 
@@ -2232,7 +2212,7 @@ static int __eb_pin_engine(struct i915_execbuffer *eb, struct intel_context *ce)
 	intel_context_exit(ce);
 	intel_context_timeline_unlock(tl);
 err_unpin:
-	__eb_unpin_context(eb, ce);
+	intel_context_unpin(ce);
 	return err;
 }
 
@@ -2245,7 +2225,7 @@ static void eb_unpin_engine(struct i915_execbuffer *eb)
 	intel_context_exit(ce);
 	mutex_unlock(&tl->mutex);
 
-	__eb_unpin_context(eb, ce);
+	intel_context_unpin(ce);
 }
 
 static unsigned int
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_mman.c b/drivers/gpu/drm/i915/gem/i915_gem_mman.c
index 82db2b783123..9a8c307c5aeb 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_mman.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_mman.c
@@ -251,16 +251,6 @@ vm_fault_t i915_gem_fault(struct vm_fault *vmf)
 		goto err_rpm;
 	}
 
-	ret = i915_mutex_lock_interruptible(dev);
-	if (ret)
-		goto err_reset;
-
-	/* Access to snoopable pages through the GTT is incoherent. */
-	if (obj->cache_level != I915_CACHE_NONE && !HAS_LLC(i915)) {
-		ret = -EFAULT;
-		goto err_unlock;
-	}
-
 	/* Now pin it into the GTT as needed */
 	vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0,
 				       PIN_MAPPABLE |
@@ -293,7 +283,13 @@ vm_fault_t i915_gem_fault(struct vm_fault *vmf)
 	}
 	if (IS_ERR(vma)) {
 		ret = PTR_ERR(vma);
-		goto err_unlock;
+		goto err_reset;
+	}
+
+	/* Access to snoopable pages through the GTT is incoherent. */
+	if (obj->cache_level != I915_CACHE_NONE && !HAS_LLC(i915)) {
+		ret = -EFAULT;
+		goto err_unpin;
 	}
 
 	ret = i915_vma_pin_fence(vma);
@@ -321,14 +317,12 @@ vm_fault_t i915_gem_fault(struct vm_fault *vmf)
 		intel_wakeref_auto(&i915->ggtt.userfault_wakeref,
 				   msecs_to_jiffies_timeout(CONFIG_DRM_I915_USERFAULT_AUTOSUSPEND));
 
-	i915_vma_set_ggtt_write(vma);
-
+	if (write)
+		i915_vma_set_ggtt_write(vma);
 err_fence:
 	i915_vma_unpin_fence(vma);
 err_unpin:
 	__i915_vma_unpin(vma);
-err_unlock:
-	mutex_unlock(&dev->struct_mutex);
 err_reset:
 	intel_gt_reset_unlock(ggtt->vm.gt, srcu);
 err_rpm:
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.c b/drivers/gpu/drm/i915/gem/i915_gem_object.c
index 0ef60dae23a7..dbf9be9a79f4 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_object.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_object.c
@@ -155,21 +155,30 @@ static void __i915_gem_free_objects(struct drm_i915_private *i915,
 
 	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
 	llist_for_each_entry_safe(obj, on, freed, freed) {
-		struct i915_vma *vma, *vn;
-
 		trace_i915_gem_object_destroy(obj);
 
-		mutex_lock(&i915->drm.struct_mutex);
-
-		list_for_each_entry_safe(vma, vn, &obj->vma.list, obj_link) {
-			GEM_BUG_ON(i915_vma_is_active(vma));
-			atomic_and(~I915_VMA_PIN_MASK, &vma->flags);
-			i915_vma_destroy(vma);
+		if (!list_empty(&obj->vma.list)) {
+			struct i915_vma *vma;
+
+			/*
+			 * Note that the vma keeps an object reference while
+			 * it is active, so it *should* not sleep while we
+			 * destroy it. Our debug code errs insits it *might*.
+			 * For the moment, play along.
+			 */
+			spin_lock(&obj->vma.lock);
+			while ((vma = list_first_entry_or_null(&obj->vma.list,
+							       struct i915_vma,
+							       obj_link))) {
+				GEM_BUG_ON(vma->obj != obj);
+				spin_unlock(&obj->vma.lock);
+
+				i915_vma_destroy(vma);
+
+				spin_lock(&obj->vma.lock);
+			}
+			spin_unlock(&obj->vma.lock);
 		}
-		GEM_BUG_ON(!list_empty(&obj->vma.list));
-		GEM_BUG_ON(!RB_EMPTY_ROOT(&obj->vma.tree));
-
-		mutex_unlock(&i915->drm.struct_mutex);
 
 		GEM_BUG_ON(atomic_read(&obj->bind_count));
 		GEM_BUG_ON(obj->userfault_count);
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.h b/drivers/gpu/drm/i915/gem/i915_gem_object.h
index 29b9eddc4c7f..a78af25dce36 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_object.h
+++ b/drivers/gpu/drm/i915/gem/i915_gem_object.h
@@ -106,6 +106,11 @@ static inline void i915_gem_object_lock(struct drm_i915_gem_object *obj)
 	dma_resv_lock(obj->base.resv, NULL);
 }
 
+static inline bool i915_gem_object_trylock(struct drm_i915_gem_object *obj)
+{
+	return dma_resv_trylock(obj->base.resv);
+}
+
 static inline int
 i915_gem_object_lock_interruptible(struct drm_i915_gem_object *obj)
 {
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_shrinker.c b/drivers/gpu/drm/i915/gem/i915_gem_shrinker.c
index d2c05d752909..fd604ea12f7f 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_shrinker.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_shrinker.c
@@ -16,40 +16,6 @@
 
 #include "i915_trace.h"
 
-static bool shrinker_lock(struct drm_i915_private *i915,
-			  unsigned int flags,
-			  bool *unlock)
-{
-	struct mutex *m = &i915->drm.struct_mutex;
-
-	switch (mutex_trylock_recursive(m)) {
-	case MUTEX_TRYLOCK_RECURSIVE:
-		*unlock = false;
-		return true;
-
-	case MUTEX_TRYLOCK_FAILED:
-		*unlock = false;
-		if (flags & I915_SHRINK_ACTIVE &&
-		    mutex_lock_killable_nested(m, I915_MM_SHRINKER) == 0)
-			*unlock = true;
-		return *unlock;
-
-	case MUTEX_TRYLOCK_SUCCESS:
-		*unlock = true;
-		return true;
-	}
-
-	BUG();
-}
-
-static void shrinker_unlock(struct drm_i915_private *i915, bool unlock)
-{
-	if (!unlock)
-		return;
-
-	mutex_unlock(&i915->drm.struct_mutex);
-}
-
 static bool swap_available(void)
 {
 	return get_nr_swap_pages() > 0;
@@ -155,10 +121,6 @@ i915_gem_shrink(struct drm_i915_private *i915,
 	intel_wakeref_t wakeref = 0;
 	unsigned long count = 0;
 	unsigned long scanned = 0;
-	bool unlock;
-
-	if (!shrinker_lock(i915, shrink, &unlock))
-		return 0;
 
 	/*
 	 * When shrinking the active list, we should also consider active
@@ -268,8 +230,6 @@ i915_gem_shrink(struct drm_i915_private *i915,
 	if (shrink & I915_SHRINK_BOUND)
 		intel_runtime_pm_put(&i915->runtime_pm, wakeref);
 
-	shrinker_unlock(i915, unlock);
-
 	if (nr_scanned)
 		*nr_scanned += scanned;
 	return count;
@@ -339,19 +299,14 @@ i915_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc)
 	struct drm_i915_private *i915 =
 		container_of(shrinker, struct drm_i915_private, mm.shrinker);
 	unsigned long freed;
-	bool unlock;
 
 	sc->nr_scanned = 0;
 
-	if (!shrinker_lock(i915, 0, &unlock))
-		return SHRINK_STOP;
-
 	freed = i915_gem_shrink(i915,
 				sc->nr_to_scan,
 				&sc->nr_scanned,
 				I915_SHRINK_BOUND |
-				I915_SHRINK_UNBOUND |
-				I915_SHRINK_WRITEBACK);
+				I915_SHRINK_UNBOUND);
 	if (sc->nr_scanned < sc->nr_to_scan && current_is_kswapd()) {
 		intel_wakeref_t wakeref;
 
@@ -366,8 +321,6 @@ i915_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc)
 		}
 	}
 
-	shrinker_unlock(i915, unlock);
-
 	return sc->nr_scanned ? freed : SHRINK_STOP;
 }
 
@@ -384,6 +337,7 @@ i915_gem_shrinker_oom(struct notifier_block *nb, unsigned long event, void *ptr)
 	freed_pages = 0;
 	with_intel_runtime_pm(&i915->runtime_pm, wakeref)
 		freed_pages += i915_gem_shrink(i915, -1UL, NULL,
+					       I915_SHRINK_ACTIVE |
 					       I915_SHRINK_BOUND |
 					       I915_SHRINK_UNBOUND |
 					       I915_SHRINK_WRITEBACK);
@@ -419,10 +373,6 @@ i915_gem_shrinker_vmap(struct notifier_block *nb, unsigned long event, void *ptr
 	struct i915_vma *vma, *next;
 	unsigned long freed_pages = 0;
 	intel_wakeref_t wakeref;
-	bool unlock;
-
-	if (!shrinker_lock(i915, 0, &unlock))
-		return NOTIFY_DONE;
 
 	with_intel_runtime_pm(&i915->runtime_pm, wakeref)
 		freed_pages += i915_gem_shrink(i915, -1UL, NULL,
@@ -439,15 +389,11 @@ i915_gem_shrinker_vmap(struct notifier_block *nb, unsigned long event, void *ptr
 		if (!vma->iomap || i915_vma_is_active(vma))
 			continue;
 
-		mutex_unlock(&i915->ggtt.vm.mutex);
 		if (i915_vma_unbind(vma) == 0)
 			freed_pages += count;
-		mutex_lock(&i915->ggtt.vm.mutex);
 	}
 	mutex_unlock(&i915->ggtt.vm.mutex);
 
-	shrinker_unlock(i915, unlock);
-
 	*(unsigned long *)ptr += freed_pages;
 	return NOTIFY_DONE;
 }
@@ -490,22 +436,9 @@ void i915_gem_shrinker_taints_mutex(struct drm_i915_private *i915,
 
 	fs_reclaim_acquire(GFP_KERNEL);
 
-	/*
-	 * As we invariably rely on the struct_mutex within the shrinker,
-	 * but have a complicated recursion dance, taint all the mutexes used
-	 * within the shrinker with the struct_mutex. For completeness, we
-	 * taint with all subclass of struct_mutex, even though we should
-	 * only need tainting by I915_MM_NORMAL to catch possible ABBA
-	 * deadlocks from using struct_mutex inside @mutex.
-	 */
-	mutex_acquire(&i915->drm.struct_mutex.dep_map,
-		      I915_MM_SHRINKER, 0, _RET_IP_);
-
 	mutex_acquire(&mutex->dep_map, 0, 0, _RET_IP_);
 	mutex_release(&mutex->dep_map, 0, _RET_IP_);
 
-	mutex_release(&i915->drm.struct_mutex.dep_map, 0, _RET_IP_);
-
 	fs_reclaim_release(GFP_KERNEL);
 
 	if (unlock)
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_stolen.c b/drivers/gpu/drm/i915/gem/i915_gem_stolen.c
index 0d81de1461b4..d2aed728ad8d 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_stolen.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_stolen.c
@@ -621,8 +621,6 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_i915_private *dev_priv
 	if (!drm_mm_initialized(&dev_priv->mm.stolen))
 		return NULL;
 
-	lockdep_assert_held(&dev_priv->drm.struct_mutex);
-
 	DRM_DEBUG_DRIVER("creating preallocated stolen object: stolen_offset=%pa, gtt_offset=%pa, size=%pa\n",
 			 &stolen_offset, &gtt_offset, &size);
 
@@ -674,21 +672,25 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_i915_private *dev_priv
 	 * setting up the GTT space. The actual reservation will occur
 	 * later.
 	 */
+	mutex_lock(&ggtt->vm.mutex);
 	ret = i915_gem_gtt_reserve(&ggtt->vm, &vma->node,
 				   size, gtt_offset, obj->cache_level,
 				   0);
 	if (ret) {
 		DRM_DEBUG_DRIVER("failed to allocate stolen GTT space\n");
+		mutex_unlock(&ggtt->vm.mutex);
 		goto err_pages;
 	}
 
 	GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
 
+	GEM_BUG_ON(vma->pages);
 	vma->pages = obj->mm.pages;
+	atomic_set(&vma->pages_count, I915_VMA_PAGES_ACTIVE);
+
 	set_bit(I915_VMA_GLOBAL_BIND_BIT, __i915_vma_flags(vma));
 	__i915_vma_set_map_and_fenceable(vma);
 
-	mutex_lock(&ggtt->vm.mutex);
 	list_add_tail(&vma->vm_link, &ggtt->vm.bound_list);
 	mutex_unlock(&ggtt->vm.mutex);
 
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_tiling.c b/drivers/gpu/drm/i915/gem/i915_gem_tiling.c
index ca0c2f451742..b9cfae0e4435 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_tiling.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_tiling.c
@@ -181,22 +181,25 @@ static int
 i915_gem_object_fence_prepare(struct drm_i915_gem_object *obj,
 			      int tiling_mode, unsigned int stride)
 {
+	struct i915_ggtt *ggtt = &to_i915(obj->base.dev)->ggtt;
 	struct i915_vma *vma;
-	int ret;
+	int ret = 0;
 
 	if (tiling_mode == I915_TILING_NONE)
 		return 0;
 
+	mutex_lock(&ggtt->vm.mutex);
 	for_each_ggtt_vma(vma, obj) {
 		if (i915_vma_fence_prepare(vma, tiling_mode, stride))
 			continue;
 
 		ret = i915_vma_unbind(vma);
 		if (ret)
-			return ret;
+			break;
 	}
+	mutex_unlock(&ggtt->vm.mutex);
 
-	return 0;
+	return ret;
 }
 
 int
@@ -212,7 +215,6 @@ i915_gem_object_set_tiling(struct drm_i915_gem_object *obj,
 
 	GEM_BUG_ON(!i915_tiling_ok(obj, tiling, stride));
 	GEM_BUG_ON(!stride ^ (tiling == I915_TILING_NONE));
-	lockdep_assert_held(&i915->drm.struct_mutex);
 
 	if ((tiling | stride) == obj->tiling_and_stride)
 		return 0;
@@ -364,12 +366,7 @@ i915_gem_set_tiling_ioctl(struct drm_device *dev, void *data,
 		}
 	}
 
-	err = mutex_lock_interruptible(&dev->struct_mutex);
-	if (err)
-		goto err;
-
 	err = i915_gem_object_set_tiling(obj, args->tiling_mode, args->stride);
-	mutex_unlock(&dev->struct_mutex);
 
 	/* We have to maintain this existing ABI... */
 	args->stride = i915_gem_object_get_stride(obj);
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_userptr.c b/drivers/gpu/drm/i915/gem/i915_gem_userptr.c
index 74da35611d7c..cd36236e3faf 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_userptr.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_userptr.c
@@ -92,7 +92,6 @@ userptr_mn_invalidate_range_start(struct mmu_notifier *_mn,
 	struct i915_mmu_notifier *mn =
 		container_of(_mn, struct i915_mmu_notifier, mn);
 	struct interval_tree_node *it;
-	struct mutex *unlock = NULL;
 	unsigned long end;
 	int ret = 0;
 
@@ -129,33 +128,13 @@ userptr_mn_invalidate_range_start(struct mmu_notifier *_mn,
 		}
 		spin_unlock(&mn->lock);
 
-		if (!unlock) {
-			unlock = &mn->mm->i915->drm.struct_mutex;
-
-			switch (mutex_trylock_recursive(unlock)) {
-			default:
-			case MUTEX_TRYLOCK_FAILED:
-				if (mutex_lock_killable_nested(unlock, I915_MM_SHRINKER)) {
-					i915_gem_object_put(obj);
-					return -EINTR;
-				}
-				/* fall through */
-			case MUTEX_TRYLOCK_SUCCESS:
-				break;
-
-			case MUTEX_TRYLOCK_RECURSIVE:
-				unlock = ERR_PTR(-EEXIST);
-				break;
-			}
-		}
-
 		ret = i915_gem_object_unbind(obj,
 					     I915_GEM_OBJECT_UNBIND_ACTIVE);
 		if (ret == 0)
 			ret = __i915_gem_object_put_pages(obj, I915_MM_SHRINKER);
 		i915_gem_object_put(obj);
 		if (ret)
-			goto unlock;
+			return ret;
 
 		spin_lock(&mn->lock);
 
@@ -168,10 +147,6 @@ userptr_mn_invalidate_range_start(struct mmu_notifier *_mn,
 	}
 	spin_unlock(&mn->lock);
 
-unlock:
-	if (!IS_ERR_OR_NULL(unlock))
-		mutex_unlock(unlock);
-
 	return ret;
 
 }
diff --git a/drivers/gpu/drm/i915/gem/selftests/huge_pages.c b/drivers/gpu/drm/i915/gem/selftests/huge_pages.c
index c5cea4379216..cd771147b41f 100644
--- a/drivers/gpu/drm/i915/gem/selftests/huge_pages.c
+++ b/drivers/gpu/drm/i915/gem/selftests/huge_pages.c
@@ -25,6 +25,17 @@ static const unsigned int page_sizes[] = {
 	I915_GTT_PAGE_SIZE_4K,
 };
 
+static int unlocked_vma_unbind(struct i915_vma *vma)
+{
+	int ret;
+
+	mutex_lock(&vma->vm->mutex);
+	ret = i915_vma_unbind(vma);
+	mutex_unlock(&vma->vm->mutex);
+
+	return ret;
+}
+
 static unsigned int get_largest_page_size(struct drm_i915_private *i915,
 					  u64 rem)
 {
@@ -333,7 +344,11 @@ static int igt_check_page_sizes(struct i915_vma *vma)
 	struct drm_i915_private *i915 = vma->vm->i915;
 	unsigned int supported = INTEL_INFO(i915)->page_sizes;
 	struct drm_i915_gem_object *obj = vma->obj;
-	int err = 0;
+	int err;
+
+	err = i915_active_wait(&vma->active);
+	if (err)
+		return err;
 
 	if (!HAS_PAGE_SIZES(i915, vma->page_sizes.sg)) {
 		pr_err("unsupported page_sizes.sg=%u, supported=%u\n",
@@ -526,7 +541,7 @@ static int igt_mock_ppgtt_misaligned_dma(void *arg)
 		 * pages.
 		 */
 		for (offset = 4096; offset < page_size; offset += 4096) {
-			err = i915_vma_unbind(vma);
+			err = unlocked_vma_unbind(vma);
 			if (err) {
 				i915_vma_close(vma);
 				goto out_unpin;
@@ -941,7 +956,7 @@ static int __igt_write_huge(struct intel_context *ce,
 	if (IS_ERR(vma))
 		return PTR_ERR(vma);
 
-	err = i915_vma_unbind(vma);
+	err = unlocked_vma_unbind(vma);
 	if (err)
 		goto out_vma_close;
 
@@ -1390,7 +1405,7 @@ static int igt_ppgtt_pin_update(void *arg)
 			goto out_unpin;
 		}
 
-		err = i915_vma_bind(vma, I915_CACHE_NONE, PIN_UPDATE);
+		err = i915_vma_bind(vma, I915_CACHE_NONE, PIN_UPDATE, NULL);
 		if (err)
 			goto out_unpin;
 
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 da54a718c712..aa67c02ba98c 100644
--- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c
+++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c
@@ -747,10 +747,7 @@ emit_rpcs_query(struct drm_i915_gem_object *obj,
 	if (err)
 		goto skip_request;
 
-	i915_vma_unpin(batch);
-	i915_vma_close(batch);
-	i915_vma_put(batch);
-
+	i915_vma_unpin_and_release(&batch, 0);
 	i915_vma_unpin(vma);
 
 	*rq_out = i915_request_get(rq);
@@ -764,8 +761,7 @@ emit_rpcs_query(struct drm_i915_gem_object *obj,
 err_request:
 	i915_request_add(rq);
 err_batch:
-	i915_vma_unpin(batch);
-	i915_vma_put(batch);
+	i915_vma_unpin_and_release(&batch, 0);
 err_vma:
 	i915_vma_unpin(vma);
 
@@ -1309,9 +1305,7 @@ static int write_to_scratch(struct i915_gem_context *ctx,
 	if (err)
 		goto skip_request;
 
-	i915_vma_unpin(vma);
-	i915_vma_close(vma);
-	i915_vma_put(vma);
+	i915_vma_unpin_and_release(&vma, 0);
 
 	i915_request_add(rq);
 
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 1d27babff0ce..9c217dfe96a9 100644
--- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c
+++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c
@@ -205,7 +205,6 @@ static int igt_partial_tiling(void *arg)
 		goto out;
 	}
 
-	mutex_lock(&i915->drm.struct_mutex);
 	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
 
 	if (1) {
@@ -318,7 +317,6 @@ next_tiling: ;
 
 out_unlock:
 	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
-	mutex_unlock(&i915->drm.struct_mutex);
 	i915_gem_object_unpin_pages(obj);
 out:
 	i915_gem_object_put(obj);
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 ee5dc13a30b3..6718da20f35d 100644
--- a/drivers/gpu/drm/i915/gem/selftests/igt_gem_utils.c
+++ b/drivers/gpu/drm/i915/gem/selftests/igt_gem_utils.c
@@ -154,9 +154,7 @@ int igt_gpu_fill_dw(struct intel_context *ce,
 
 	i915_request_add(rq);
 
-	i915_vma_unpin(batch);
-	i915_vma_close(batch);
-	i915_vma_put(batch);
+	i915_vma_unpin_and_release(&batch, 0);
 
 	return 0;
 
@@ -165,7 +163,6 @@ int igt_gpu_fill_dw(struct intel_context *ce,
 err_request:
 	i915_request_add(rq);
 err_batch:
-	i915_vma_unpin(batch);
-	i915_vma_put(batch);
+	i915_vma_unpin_and_release(&batch, 0);
 	return err;
 }
diff --git a/drivers/gpu/drm/i915/gt/intel_gt.c b/drivers/gpu/drm/i915/gt/intel_gt.c
index d48ec9a76ed1..c2afffb94474 100644
--- a/drivers/gpu/drm/i915/gt/intel_gt.c
+++ b/drivers/gpu/drm/i915/gt/intel_gt.c
@@ -207,11 +207,12 @@ void intel_gt_flush_ggtt_writes(struct intel_gt *gt)
 
 	with_intel_runtime_pm(&i915->runtime_pm, wakeref) {
 		struct intel_uncore *uncore = gt->uncore;
+		unsigned long flags;
 
-		spin_lock_irq(&uncore->lock);
+		spin_lock_irqsave(&uncore->lock, flags);
 		intel_uncore_posting_read_fw(uncore,
 					     RING_HEAD(RENDER_RING_BASE));
-		spin_unlock_irq(&uncore->lock);
+		spin_unlock_irqrestore(&uncore->lock, flags);
 	}
 }
 
diff --git a/drivers/gpu/drm/i915/gt/intel_ringbuffer.c b/drivers/gpu/drm/i915/gt/intel_ringbuffer.c
index ac55a0d054bd..855e97ccaf9f 100644
--- a/drivers/gpu/drm/i915/gt/intel_ringbuffer.c
+++ b/drivers/gpu/drm/i915/gt/intel_ringbuffer.c
@@ -1336,15 +1336,13 @@ void intel_ring_free(struct kref *ref)
 {
 	struct intel_ring *ring = container_of(ref, typeof(*ring), ref);
 
-	i915_vma_close(ring->vma);
 	i915_vma_put(ring->vma);
-
 	kfree(ring);
 }
 
 static void __ring_context_fini(struct intel_context *ce)
 {
-	i915_gem_object_put(ce->state->obj);
+	i915_vma_put(ce->state);
 }
 
 static void ring_context_destroy(struct kref *ref)
diff --git a/drivers/gpu/drm/i915/gt/selftest_hangcheck.c b/drivers/gpu/drm/i915/gt/selftest_hangcheck.c
index a0098fc35921..e53eea1050f8 100644
--- a/drivers/gpu/drm/i915/gt/selftest_hangcheck.c
+++ b/drivers/gpu/drm/i915/gt/selftest_hangcheck.c
@@ -1127,15 +1127,14 @@ static int evict_vma(void *data)
 {
 	struct evict_vma *arg = data;
 	struct i915_address_space *vm = arg->vma->vm;
-	struct drm_i915_private *i915 = vm->i915;
 	struct drm_mm_node evict = arg->vma->node;
 	int err;
 
 	complete(&arg->completion);
 
-	mutex_lock(&i915->drm.struct_mutex);
+	mutex_lock(&vm->mutex);
 	err = i915_gem_evict_for_node(vm, &evict, 0);
-	mutex_unlock(&i915->drm.struct_mutex);
+	mutex_unlock(&vm->mutex);
 
 	return err;
 }
@@ -1143,39 +1142,33 @@ static int evict_vma(void *data)
 static int evict_fence(void *data)
 {
 	struct evict_vma *arg = data;
-	struct drm_i915_private *i915 = arg->vma->vm->i915;
 	int err;
 
 	complete(&arg->completion);
 
-	mutex_lock(&i915->drm.struct_mutex);
-
 	/* Mark the fence register as dirty to force the mmio update. */
 	err = i915_gem_object_set_tiling(arg->vma->obj, I915_TILING_Y, 512);
 	if (err) {
 		pr_err("Invalid Y-tiling settings; err:%d\n", err);
-		goto out_unlock;
+		return err;
 	}
 
 	err = i915_vma_pin(arg->vma, 0, 0, PIN_GLOBAL | PIN_MAPPABLE);
 	if (err) {
 		pr_err("Unable to pin vma for Y-tiled fence; err:%d\n", err);
-		goto out_unlock;
+		return err;
 	}
 
 	err = i915_vma_pin_fence(arg->vma);
 	i915_vma_unpin(arg->vma);
 	if (err) {
 		pr_err("Unable to pin Y-tiled fence; err:%d\n", err);
-		goto out_unlock;
+		return err;
 	}
 
 	i915_vma_unpin_fence(arg->vma);
 
-out_unlock:
-	mutex_unlock(&i915->drm.struct_mutex);
-
-	return err;
+	return 0;
 }
 
 static int __igt_reset_evict_vma(struct intel_gt *gt,
diff --git a/drivers/gpu/drm/i915/gvt/aperture_gm.c b/drivers/gpu/drm/i915/gvt/aperture_gm.c
index 5ff2437b2998..d996bbc7ea59 100644
--- a/drivers/gpu/drm/i915/gvt/aperture_gm.c
+++ b/drivers/gpu/drm/i915/gvt/aperture_gm.c
@@ -61,14 +61,14 @@ static int alloc_gm(struct intel_vgpu *vgpu, bool high_gm)
 		flags = PIN_MAPPABLE;
 	}
 
-	mutex_lock(&dev_priv->drm.struct_mutex);
+	mutex_lock(&dev_priv->ggtt.vm.mutex);
 	mmio_hw_access_pre(dev_priv);
 	ret = i915_gem_gtt_insert(&dev_priv->ggtt.vm, node,
 				  size, I915_GTT_PAGE_SIZE,
 				  I915_COLOR_UNEVICTABLE,
 				  start, end, flags);
 	mmio_hw_access_post(dev_priv);
-	mutex_unlock(&dev_priv->drm.struct_mutex);
+	mutex_unlock(&dev_priv->ggtt.vm.mutex);
 	if (ret)
 		gvt_err("fail to alloc %s gm space from host\n",
 			high_gm ? "high" : "low");
@@ -98,9 +98,9 @@ static int alloc_vgpu_gm(struct intel_vgpu *vgpu)
 
 	return 0;
 out_free_aperture:
-	mutex_lock(&dev_priv->drm.struct_mutex);
+	mutex_lock(&dev_priv->ggtt.vm.mutex);
 	drm_mm_remove_node(&vgpu->gm.low_gm_node);
-	mutex_unlock(&dev_priv->drm.struct_mutex);
+	mutex_unlock(&dev_priv->ggtt.vm.mutex);
 	return ret;
 }
 
@@ -108,10 +108,10 @@ static void free_vgpu_gm(struct intel_vgpu *vgpu)
 {
 	struct drm_i915_private *dev_priv = vgpu->gvt->dev_priv;
 
-	mutex_lock(&dev_priv->drm.struct_mutex);
+	mutex_lock(&dev_priv->ggtt.vm.mutex);
 	drm_mm_remove_node(&vgpu->gm.low_gm_node);
 	drm_mm_remove_node(&vgpu->gm.high_gm_node);
-	mutex_unlock(&dev_priv->drm.struct_mutex);
+	mutex_unlock(&dev_priv->ggtt.vm.mutex);
 }
 
 /**
diff --git a/drivers/gpu/drm/i915/i915_active.c b/drivers/gpu/drm/i915/i915_active.c
index 6a447f1d0110..6a37ed52957a 100644
--- a/drivers/gpu/drm/i915/i915_active.c
+++ b/drivers/gpu/drm/i915/i915_active.c
@@ -146,6 +146,7 @@ __active_retire(struct i915_active *ref)
 	if (!retire)
 		return;
 
+	GEM_BUG_ON(rcu_access_pointer(ref->excl));
 	rbtree_postorder_for_each_entry_safe(it, n, &root, node) {
 		GEM_BUG_ON(i915_active_request_isset(&it->base));
 		kmem_cache_free(global.slab_cache, it);
@@ -245,6 +246,8 @@ void __i915_active_init(struct drm_i915_private *i915,
 	ref->flags = 0;
 	ref->active = active;
 	ref->retire = retire;
+
+	ref->excl = NULL;
 	ref->tree = RB_ROOT;
 	ref->cache = NULL;
 	init_llist_head(&ref->preallocated_barriers);
@@ -341,6 +344,45 @@ int i915_active_ref(struct i915_active *ref,
 	return err;
 }
 
+static void excl_cb(struct dma_fence *f, struct dma_fence_cb *cb)
+{
+	struct i915_active *ref = container_of(cb, typeof(*ref), excl_cb);
+
+	RCU_INIT_POINTER(ref->excl, NULL);
+	dma_fence_put(f);
+
+	active_retire(ref);
+}
+
+void i915_active_set_exclusive(struct i915_active *ref, struct dma_fence *f)
+{
+	GEM_BUG_ON(i915_active_is_idle(ref));
+
+	dma_fence_get(f);
+
+	rcu_read_lock();
+	if (rcu_access_pointer(ref->excl)) {
+		struct dma_fence *old;
+
+		old = dma_fence_get_rcu_safe(&ref->excl);
+		if (old) {
+			if (dma_fence_remove_callback(old, &ref->excl_cb))
+				atomic_dec(&ref->count);
+			dma_fence_put(old);
+		}
+	}
+	rcu_read_unlock();
+
+	atomic_inc(&ref->count);
+	rcu_assign_pointer(ref->excl, f);
+
+	if (dma_fence_add_callback(f, &ref->excl_cb, excl_cb)) {
+		RCU_INIT_POINTER(ref->excl, NULL);
+		atomic_dec(&ref->count);
+		dma_fence_put(f);
+	}
+}
+
 int i915_active_acquire(struct i915_active *ref)
 {
 	int err;
@@ -399,6 +441,25 @@ void i915_active_ungrab(struct i915_active *ref)
 	__active_ungrab(ref);
 }
 
+static int excl_wait(struct i915_active *ref)
+{
+	struct dma_fence *old;
+	int err = 0;
+
+	if (!rcu_access_pointer(ref->excl))
+		return 0;
+
+	rcu_read_lock();
+	old = dma_fence_get_rcu_safe(&ref->excl);
+	rcu_read_unlock();
+	if (old) {
+		err = dma_fence_wait(old, true);
+		dma_fence_put(old);
+	}
+
+	return err;
+}
+
 int i915_active_wait(struct i915_active *ref)
 {
 	struct active_node *it, *n;
@@ -419,6 +480,10 @@ int i915_active_wait(struct i915_active *ref)
 		return 0;
 	}
 
+	err = excl_wait(ref);
+	if (err)
+		goto out;
+
 	rbtree_postorder_for_each_entry_safe(it, n, &ref->tree, node) {
 		if (is_barrier(&it->base)) { /* unconnected idle-barrier */
 			err = -EBUSY;
@@ -430,6 +495,7 @@ int i915_active_wait(struct i915_active *ref)
 			break;
 	}
 
+out:
 	__active_retire(ref);
 	if (err)
 		return err;
@@ -454,26 +520,22 @@ int i915_request_await_active_request(struct i915_request *rq,
 
 int i915_request_await_active(struct i915_request *rq, struct i915_active *ref)
 {
-	struct active_node *it, *n;
-	int err;
-
-	if (RB_EMPTY_ROOT(&ref->tree))
-		return 0;
+	int err = 0;
 
-	/* await allocates and so we need to avoid hitting the shrinker */
-	err = i915_active_acquire(ref);
-	if (err)
-		return err;
+	if (rcu_access_pointer(ref->excl)) {
+		struct dma_fence *fence;
 
-	mutex_lock(&ref->mutex);
-	rbtree_postorder_for_each_entry_safe(it, n, &ref->tree, node) {
-		err = i915_request_await_active_request(rq, &it->base);
-		if (err)
-			break;
+		rcu_read_lock();
+		fence = dma_fence_get_rcu_safe(&ref->excl);
+		rcu_read_unlock();
+		if (fence) {
+			err = i915_request_await_dma_fence(rq, fence);
+			dma_fence_put(fence);
+		}
 	}
-	mutex_unlock(&ref->mutex);
 
-	i915_active_release(ref);
+	/* In the future we may choose to await on all fences */
+
 	return err;
 }
 
diff --git a/drivers/gpu/drm/i915/i915_active.h b/drivers/gpu/drm/i915/i915_active.h
index f95058f99057..af3d536e26fd 100644
--- a/drivers/gpu/drm/i915/i915_active.h
+++ b/drivers/gpu/drm/i915/i915_active.h
@@ -373,6 +373,13 @@ int i915_active_ref(struct i915_active *ref,
 		    struct intel_timeline *tl,
 		    struct i915_request *rq);
 
+void i915_active_set_exclusive(struct i915_active *ref, struct dma_fence *f);
+
+static inline bool i915_active_has_exclusive(struct i915_active *ref)
+{
+	return rcu_access_pointer(ref->excl);
+}
+
 int i915_active_wait(struct i915_active *ref);
 
 int i915_request_await_active(struct i915_request *rq,
diff --git a/drivers/gpu/drm/i915/i915_active_types.h b/drivers/gpu/drm/i915/i915_active_types.h
index 1854e7d168c1..86e7a232ea3c 100644
--- a/drivers/gpu/drm/i915/i915_active_types.h
+++ b/drivers/gpu/drm/i915/i915_active_types.h
@@ -8,6 +8,7 @@
 #define _I915_ACTIVE_TYPES_H_
 
 #include <linux/atomic.h>
+#include <linux/dma-fence.h>
 #include <linux/llist.h>
 #include <linux/mutex.h>
 #include <linux/rbtree.h>
@@ -51,6 +52,10 @@ struct i915_active {
 	struct mutex mutex;
 	atomic_t count;
 
+	/* Preallocated "exclusive" node */
+	struct dma_fence __rcu *excl;
+	struct dma_fence_cb excl_cb;
+
 	unsigned long flags;
 #define I915_ACTIVE_GRAB_BIT 0
 
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 814f62fca727..3eed2efa8d13 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -62,20 +62,31 @@
 #include "intel_pm.h"
 
 static int
-insert_mappable_node(struct i915_ggtt *ggtt,
-                     struct drm_mm_node *node, u32 size)
+insert_mappable_node(struct i915_ggtt *ggtt, struct drm_mm_node *node, u32 size)
 {
+	int err;
+
+	err = mutex_lock_interruptible(&ggtt->vm.mutex);
+	if (err)
+		return err;
+
 	memset(node, 0, sizeof(*node));
-	return drm_mm_insert_node_in_range(&ggtt->vm.mm, node,
-					   size, 0, I915_COLOR_UNEVICTABLE,
-					   0, ggtt->mappable_end,
-					   DRM_MM_INSERT_LOW);
+	err = drm_mm_insert_node_in_range(&ggtt->vm.mm, node,
+					  size, 0, I915_COLOR_UNEVICTABLE,
+					  0, ggtt->mappable_end,
+					  DRM_MM_INSERT_LOW);
+
+	mutex_unlock(&ggtt->vm.mutex);
+
+	return err;
 }
 
 static void
-remove_mappable_node(struct drm_mm_node *node)
+remove_mappable_node(struct i915_ggtt *ggtt, struct drm_mm_node *node)
 {
+	mutex_lock(&ggtt->vm.mutex);
 	drm_mm_remove_node(node);
+	mutex_unlock(&ggtt->vm.mutex);
 }
 
 int
@@ -87,7 +98,8 @@ i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data,
 	struct i915_vma *vma;
 	u64 pinned;
 
-	mutex_lock(&ggtt->vm.mutex);
+	if (mutex_lock_interruptible(&ggtt->vm.mutex))
+		return -EINTR;
 
 	pinned = ggtt->vm.reserved;
 	list_for_each_entry(vma, &ggtt->vm.bound_list, vm_link)
@@ -109,20 +121,31 @@ int i915_gem_object_unbind(struct drm_i915_gem_object *obj,
 	LIST_HEAD(still_in_list);
 	int ret = 0;
 
-	lockdep_assert_held(&obj->base.dev->struct_mutex);
-
 	spin_lock(&obj->vma.lock);
 	while (!ret && (vma = list_first_entry_or_null(&obj->vma.list,
 						       struct i915_vma,
 						       obj_link))) {
+		struct i915_address_space *vm = vma->vm;
+
+		ret = -EBUSY;
+		if (!i915_vm_tryopen(vm))
+			break;
+
 		list_move_tail(&vma->obj_link, &still_in_list);
 		spin_unlock(&obj->vma.lock);
 
-		ret = -EBUSY;
 		if (flags & I915_GEM_OBJECT_UNBIND_ACTIVE ||
-		    !i915_vma_is_active(vma))
-			ret = i915_vma_unbind(vma);
+		    !i915_vma_is_active(vma)) {
+			struct i915_address_space *vm = vma->vm;
+
+			ret = mutex_lock_interruptible(&vm->mutex);
+			if (!ret) {
+				ret = i915_vma_unbind(vma);
+				mutex_unlock(&vm->mutex);
+			}
+		}
 
+		i915_vm_close(vm);
 		spin_lock(&obj->vma.lock);
 	}
 	list_splice(&still_in_list, &obj->vma.list);
@@ -338,10 +361,6 @@ i915_gem_gtt_pread(struct drm_i915_gem_object *obj,
 	u64 remain, offset;
 	int ret;
 
-	ret = mutex_lock_interruptible(&i915->drm.struct_mutex);
-	if (ret)
-		return ret;
-
 	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
 	vma = ERR_PTR(-ENODEV);
 	if (!i915_gem_object_is_tiled(obj))
@@ -355,12 +374,10 @@ i915_gem_gtt_pread(struct drm_i915_gem_object *obj,
 	} else {
 		ret = insert_mappable_node(ggtt, &node, PAGE_SIZE);
 		if (ret)
-			goto out_unlock;
+			goto out_rpm;
 		GEM_BUG_ON(!drm_mm_node_allocated(&node));
 	}
 
-	mutex_unlock(&i915->drm.struct_mutex);
-
 	ret = i915_gem_object_lock_interruptible(obj);
 	if (ret)
 		goto out_unpin;
@@ -414,17 +431,14 @@ i915_gem_gtt_pread(struct drm_i915_gem_object *obj,
 
 	i915_gem_object_unlock_fence(obj, fence);
 out_unpin:
-	mutex_lock(&i915->drm.struct_mutex);
 	if (drm_mm_node_allocated(&node)) {
 		ggtt->vm.clear_range(&ggtt->vm, node.start, node.size);
-		remove_mappable_node(&node);
+		remove_mappable_node(ggtt, &node);
 	} else {
 		i915_vma_unpin(vma);
 	}
-out_unlock:
+out_rpm:
 	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
-	mutex_unlock(&i915->drm.struct_mutex);
-
 	return ret;
 }
 
@@ -531,10 +545,6 @@ i915_gem_gtt_pwrite_fast(struct drm_i915_gem_object *obj,
 	void __user *user_data;
 	int ret;
 
-	ret = mutex_lock_interruptible(&i915->drm.struct_mutex);
-	if (ret)
-		return ret;
-
 	if (i915_gem_object_has_struct_page(obj)) {
 		/*
 		 * Avoid waking the device up if we can fallback, as
@@ -544,10 +554,8 @@ i915_gem_gtt_pwrite_fast(struct drm_i915_gem_object *obj,
 		 * using the cache bypass of indirect GGTT access.
 		 */
 		wakeref = intel_runtime_pm_get_if_in_use(rpm);
-		if (!wakeref) {
-			ret = -EFAULT;
-			goto out_unlock;
-		}
+		if (!wakeref)
+			return -EFAULT;
 	} else {
 		/* No backing pages, no fallback, we must force GGTT access */
 		wakeref = intel_runtime_pm_get(rpm);
@@ -569,8 +577,6 @@ i915_gem_gtt_pwrite_fast(struct drm_i915_gem_object *obj,
 		GEM_BUG_ON(!drm_mm_node_allocated(&node));
 	}
 
-	mutex_unlock(&i915->drm.struct_mutex);
-
 	ret = i915_gem_object_lock_interruptible(obj);
 	if (ret)
 		goto out_unpin;
@@ -634,18 +640,15 @@ i915_gem_gtt_pwrite_fast(struct drm_i915_gem_object *obj,
 
 	i915_gem_object_unlock_fence(obj, fence);
 out_unpin:
-	mutex_lock(&i915->drm.struct_mutex);
 	intel_gt_flush_ggtt_writes(ggtt->vm.gt);
 	if (drm_mm_node_allocated(&node)) {
 		ggtt->vm.clear_range(&ggtt->vm, node.start, node.size);
-		remove_mappable_node(&node);
+		remove_mappable_node(ggtt, &node);
 	} else {
 		i915_vma_unpin(vma);
 	}
 out_rpm:
 	intel_runtime_pm_put(rpm, wakeref);
-out_unlock:
-	mutex_unlock(&i915->drm.struct_mutex);
 	return ret;
 }
 
@@ -967,8 +970,6 @@ i915_gem_object_ggtt_pin(struct drm_i915_gem_object *obj,
 	struct i915_vma *vma;
 	int ret;
 
-	lockdep_assert_held(&obj->base.dev->struct_mutex);
-
 	if (flags & PIN_MAPPABLE &&
 	    (!view || view->type == I915_GGTT_VIEW_NORMAL)) {
 		/* If the required space is larger than the available
@@ -1015,14 +1016,9 @@ i915_gem_object_ggtt_pin(struct drm_i915_gem_object *obj,
 				return ERR_PTR(-ENOSPC);
 		}
 
-		WARN(i915_vma_is_pinned(vma),
-		     "bo is already pinned in ggtt with incorrect alignment:"
-		     " offset=%08x, req.alignment=%llx,"
-		     " req.map_and_fenceable=%d, vma->map_and_fenceable=%d\n",
-		     i915_ggtt_offset(vma), alignment,
-		     !!(flags & PIN_MAPPABLE),
-		     i915_vma_is_map_and_fenceable(vma));
+		mutex_lock(&vma->vm->mutex);
 		ret = i915_vma_unbind(vma);
+		mutex_unlock(&vma->vm->mutex);
 		if (ret)
 			return ERR_PTR(ret);
 	}
@@ -1328,7 +1324,9 @@ static int __intel_engines_record_defaults(struct drm_i915_private *i915)
 		 * from the GTT to prevent such accidents and reclaim the
 		 * space.
 		 */
+		mutex_lock(&state->vm->mutex);
 		err = i915_vma_unbind(state);
+		mutex_unlock(&state->vm->mutex);
 		if (err)
 			goto out;
 
diff --git a/drivers/gpu/drm/i915/i915_gem_evict.c b/drivers/gpu/drm/i915/i915_gem_evict.c
index 7abcac3b5e2e..44f5b638fa43 100644
--- a/drivers/gpu/drm/i915/i915_gem_evict.c
+++ b/drivers/gpu/drm/i915/i915_gem_evict.c
@@ -47,8 +47,7 @@ static int ggtt_flush(struct drm_i915_private *i915)
 	 * bound by their active reference.
 	 */
 	return i915_gem_wait_for_idle(i915,
-				      I915_WAIT_INTERRUPTIBLE |
-				      I915_WAIT_LOCKED,
+				      I915_WAIT_INTERRUPTIBLE,
 				      MAX_SCHEDULE_TIMEOUT);
 }
 
@@ -104,7 +103,7 @@ i915_gem_evict_something(struct i915_address_space *vm,
 	struct i915_vma *active;
 	int ret;
 
-	lockdep_assert_held(&vm->i915->drm.struct_mutex);
+	lockdep_assert_held(&vm->mutex);
 	trace_i915_gem_evict(vm, min_size, alignment, flags);
 
 	/*
@@ -127,15 +126,6 @@ i915_gem_evict_something(struct i915_address_space *vm,
 				    min_size, alignment, cache_level,
 				    start, end, mode);
 
-	/*
-	 * Retire before we search the active list. Although we have
-	 * reasonable accuracy in our retirement lists, we may have
-	 * a stray pin (preventing eviction) that can only be resolved by
-	 * retiring.
-	 */
-	if (!(flags & PIN_NONBLOCK))
-		i915_retire_requests(dev_priv);
-
 search_again:
 	active = NULL;
 	INIT_LIST_HEAD(&eviction_list);
@@ -269,7 +259,7 @@ int i915_gem_evict_for_node(struct i915_address_space *vm,
 	bool check_color;
 	int ret = 0;
 
-	lockdep_assert_held(&vm->i915->drm.struct_mutex);
+	lockdep_assert_held(&vm->mutex);
 	GEM_BUG_ON(!IS_ALIGNED(start, I915_GTT_PAGE_SIZE));
 	GEM_BUG_ON(!IS_ALIGNED(end, I915_GTT_PAGE_SIZE));
 
@@ -375,7 +365,7 @@ int i915_gem_evict_vm(struct i915_address_space *vm)
 	struct i915_vma *vma, *next;
 	int ret;
 
-	lockdep_assert_held(&vm->i915->drm.struct_mutex);
+	lockdep_assert_held(&vm->mutex);
 	trace_i915_gem_evict_vm(vm);
 
 	/* Switch back to the default context in order to unpin
@@ -390,7 +380,6 @@ int i915_gem_evict_vm(struct i915_address_space *vm)
 	}
 
 	INIT_LIST_HEAD(&eviction_list);
-	mutex_lock(&vm->mutex);
 	list_for_each_entry(vma, &vm->bound_list, vm_link) {
 		if (i915_vma_is_pinned(vma))
 			continue;
@@ -398,7 +387,6 @@ int i915_gem_evict_vm(struct i915_address_space *vm)
 		__i915_vma_pin(vma);
 		list_add(&vma->evict_link, &eviction_list);
 	}
-	mutex_unlock(&vm->mutex);
 
 	ret = 0;
 	list_for_each_entry_safe(vma, next, &eviction_list, evict_link) {
diff --git a/drivers/gpu/drm/i915/i915_gem_fence_reg.c b/drivers/gpu/drm/i915/i915_gem_fence_reg.c
index 615a9f4ef30c..414d839668d7 100644
--- a/drivers/gpu/drm/i915/i915_gem_fence_reg.c
+++ b/drivers/gpu/drm/i915/i915_gem_fence_reg.c
@@ -237,6 +237,7 @@ static int fence_update(struct i915_fence_reg *fence,
 
 	old = xchg(&fence->vma, NULL);
 	if (old) {
+		/* XXX Ideally we would move the waiting to outside the mutex */
 		ret = i915_active_wait(&old->active);
 		if (ret) {
 			fence->vma = old;
@@ -331,13 +332,15 @@ static struct i915_fence_reg *fence_find(struct drm_i915_private *i915)
 	return ERR_PTR(-EDEADLK);
 }
 
-static int __i915_vma_pin_fence(struct i915_vma *vma)
+int __i915_vma_pin_fence(struct i915_vma *vma)
 {
 	struct i915_ggtt *ggtt = i915_vm_to_ggtt(vma->vm);
 	struct i915_fence_reg *fence;
 	struct i915_vma *set = i915_gem_object_is_tiled(vma->obj) ? vma : NULL;
 	int err;
 
+	lockdep_assert_held(&vma->vm->mutex);
+
 	/* Just update our place in the LRU if our fence is getting reused. */
 	if (vma->fence) {
 		fence = vma->fence;
diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
index 56d27cf09a3d..b2e4827788fc 100644
--- a/drivers/gpu/drm/i915/i915_gem_gtt.c
+++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
@@ -150,16 +150,18 @@ static void gmch_ggtt_invalidate(struct i915_ggtt *ggtt)
 
 static int ppgtt_bind_vma(struct i915_vma *vma,
 			  enum i915_cache_level cache_level,
-			  u32 unused)
+			  u32 flags)
 {
 	u32 pte_flags;
 	int err;
 
-	if (!i915_vma_is_bound(vma, I915_VMA_LOCAL_BIND)) {
+	if (flags & I915_VMA_ALLOC) {
 		err = vma->vm->allocate_va_range(vma->vm,
 						 vma->node.start, vma->size);
 		if (err)
 			return err;
+
+		set_bit(I915_VMA_ALLOC_BIT, __i915_vma_flags(vma));
 	}
 
 	/* Applicable to VLV, and gen8+ */
@@ -167,6 +169,7 @@ static int ppgtt_bind_vma(struct i915_vma *vma,
 	if (i915_gem_object_is_readonly(vma->obj))
 		pte_flags |= PTE_READ_ONLY;
 
+	GEM_BUG_ON(!test_bit(I915_VMA_ALLOC_BIT, __i915_vma_flags(vma)));
 	vma->vm->insert_entries(vma->vm, vma, cache_level, pte_flags);
 	wmb();
 
@@ -175,7 +178,8 @@ static int ppgtt_bind_vma(struct i915_vma *vma,
 
 static void ppgtt_unbind_vma(struct i915_vma *vma)
 {
-	vma->vm->clear_range(vma->vm, vma->node.start, vma->size);
+	if (test_and_clear_bit(I915_VMA_ALLOC_BIT, __i915_vma_flags(vma)))
+		vma->vm->clear_range(vma->vm, vma->node.start, vma->size);
 }
 
 static int ppgtt_set_pages(struct i915_vma *vma)
@@ -503,15 +507,25 @@ static void i915_address_space_fini(struct i915_address_space *vm)
 	mutex_destroy(&vm->mutex);
 }
 
-static void ppgtt_destroy_vma(struct i915_address_space *vm)
+void __i915_vm_close(struct i915_address_space *vm)
 {
 	struct i915_vma *vma, *vn;
 
-	mutex_lock(&vm->i915->drm.struct_mutex);
-	list_for_each_entry_safe(vma, vn, &vm->bound_list, vm_link)
+	mutex_lock(&vm->mutex);
+	list_for_each_entry_safe(vma, vn, &vm->bound_list, vm_link) {
+		struct drm_i915_gem_object *obj = vma->obj;
+
+		if (!kref_get_unless_zero(&obj->base.refcount))
+			continue;
+
+		atomic_and(~I915_VMA_PIN_MASK, &vma->flags);
+		WARN_ON(i915_vma_unbind(vma));
 		i915_vma_destroy(vma);
+
+		i915_gem_object_put(obj);
+	}
 	GEM_BUG_ON(!list_empty(&vm->bound_list));
-	mutex_unlock(&vm->i915->drm.struct_mutex);
+	mutex_unlock(&vm->mutex);
 }
 
 static void __i915_vm_release(struct work_struct *work)
@@ -519,8 +533,6 @@ static void __i915_vm_release(struct work_struct *work)
 	struct i915_address_space *vm =
 		container_of(work, struct i915_address_space, rcu.work);
 
-	ppgtt_destroy_vma(vm);
-
 	vm->cleanup(vm);
 	i915_address_space_fini(vm);
 
@@ -535,7 +547,6 @@ void i915_vm_release(struct kref *kref)
 	GEM_BUG_ON(i915_is_ggtt(vm));
 	trace_i915_ppgtt_release(vm);
 
-	vm->closed = true;
 	queue_rcu_work(vm->i915->wq, &vm->rcu);
 }
 
@@ -543,6 +554,7 @@ static 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);
 
 	/*
 	 * The vm->mutex must be reclaim safe (for use in the shrinker).
@@ -1769,12 +1781,8 @@ static void gen6_ppgtt_free_pd(struct gen6_ppgtt *ppgtt)
 static void gen6_ppgtt_cleanup(struct i915_address_space *vm)
 {
 	struct gen6_ppgtt *ppgtt = to_gen6_ppgtt(i915_vm_to_ppgtt(vm));
-	struct drm_i915_private *i915 = vm->i915;
 
-	/* FIXME remove the struct_mutex to bring the locking under control */
-	mutex_lock(&i915->drm.struct_mutex);
 	i915_vma_destroy(ppgtt->vma);
-	mutex_unlock(&i915->drm.struct_mutex);
 
 	gen6_ppgtt_free_pd(ppgtt);
 	free_scratch(vm);
@@ -1863,7 +1871,8 @@ static struct i915_vma *pd_vma_create(struct gen6_ppgtt *ppgtt, int size)
 
 	i915_active_init(i915, &vma->active, NULL, NULL);
 
-	vma->vm = &ggtt->vm;
+	mutex_init(&vma->pages_mutex);
+	vma->vm = i915_vm_get(&ggtt->vm);
 	vma->ops = &pd_vma_ops;
 	vma->private = ppgtt;
 
@@ -1883,7 +1892,7 @@ int gen6_ppgtt_pin(struct i915_ppgtt *base)
 	struct gen6_ppgtt *ppgtt = to_gen6_ppgtt(base);
 	int err = 0;
 
-	GEM_BUG_ON(ppgtt->base.vm.closed);
+	GEM_BUG_ON(!atomic_read(&ppgtt->base.vm.open));
 
 	/*
 	 * Workaround the limited maximum vma->pin_count and the aliasing_ppgtt
@@ -2460,14 +2469,18 @@ static int aliasing_gtt_bind_vma(struct i915_vma *vma,
 	if (flags & I915_VMA_LOCAL_BIND) {
 		struct i915_ppgtt *alias = i915_vm_to_ggtt(vma->vm)->alias;
 
-		if (!i915_vma_is_bound(vma, I915_VMA_LOCAL_BIND)) {
+		if (flags & I915_VMA_ALLOC) {
 			ret = alias->vm.allocate_va_range(&alias->vm,
 							  vma->node.start,
 							  vma->size);
 			if (ret)
 				return ret;
+
+			set_bit(I915_VMA_ALLOC_BIT, __i915_vma_flags(vma));
 		}
 
+		GEM_BUG_ON(!test_bit(I915_VMA_ALLOC_BIT,
+				     __i915_vma_flags(vma)));
 		alias->vm.insert_entries(&alias->vm, vma,
 					 cache_level, pte_flags);
 	}
@@ -2496,7 +2509,7 @@ static void aliasing_gtt_unbind_vma(struct i915_vma *vma)
 			vm->clear_range(vm, vma->node.start, vma->size);
 	}
 
-	if (i915_vma_is_bound(vma, I915_VMA_LOCAL_BIND)) {
+	if (test_and_clear_bit(I915_VMA_ALLOC_BIT, __i915_vma_flags(vma))) {
 		struct i915_address_space *vm =
 			&i915_vm_to_ggtt(vma->vm)->alias->vm;
 
@@ -2599,22 +2612,16 @@ static int init_aliasing_ppgtt(struct i915_ggtt *ggtt)
 
 static void fini_aliasing_ppgtt(struct i915_ggtt *ggtt)
 {
-	struct drm_i915_private *i915 = ggtt->vm.i915;
 	struct i915_ppgtt *ppgtt;
 
-	mutex_lock(&i915->drm.struct_mutex);
-
 	ppgtt = fetch_and_zero(&ggtt->alias);
 	if (!ppgtt)
-		goto out;
+		return;
 
 	i915_vm_put(&ppgtt->vm);
 
 	ggtt->vm.vma_ops.bind_vma   = ggtt_bind_vma;
 	ggtt->vm.vma_ops.unbind_vma = ggtt_unbind_vma;
-
-out:
-	mutex_unlock(&i915->drm.struct_mutex);
 }
 
 static int ggtt_reserve_guc_top(struct i915_ggtt *ggtt)
@@ -2731,15 +2738,14 @@ int i915_init_ggtt(struct drm_i915_private *i915)
 
 static void ggtt_cleanup_hw(struct i915_ggtt *ggtt)
 {
-	struct drm_i915_private *i915 = ggtt->vm.i915;
 	struct i915_vma *vma, *vn;
 
-	ggtt->vm.closed = true;
+	atomic_set(&ggtt->vm.open, 0);
 
 	rcu_barrier(); /* flush the RCU'ed__i915_vm_release */
-	flush_workqueue(i915->wq);
+	flush_workqueue(ggtt->vm.i915->wq);
 
-	mutex_lock(&i915->drm.struct_mutex);
+	mutex_lock(&ggtt->vm.mutex);
 
 	list_for_each_entry_safe(vma, vn, &ggtt->vm.bound_list, vm_link)
 		WARN_ON(i915_vma_unbind(vma));
@@ -2748,15 +2754,12 @@ static void ggtt_cleanup_hw(struct i915_ggtt *ggtt)
 		drm_mm_remove_node(&ggtt->error_capture);
 
 	ggtt_release_guc_top(ggtt);
-
-	if (drm_mm_initialized(&ggtt->vm.mm)) {
-		intel_vgt_deballoon(ggtt);
-		i915_address_space_fini(&ggtt->vm);
-	}
+	intel_vgt_deballoon(ggtt);
 
 	ggtt->vm.cleanup(&ggtt->vm);
 
-	mutex_unlock(&i915->drm.struct_mutex);
+	mutex_unlock(&ggtt->vm.mutex);
+	i915_address_space_fini(&ggtt->vm);
 
 	arch_phys_wc_del(ggtt->mtrr);
 	io_mapping_fini(&ggtt->iomap);
@@ -3185,9 +3188,6 @@ int i915_ggtt_probe_hw(struct drm_i915_private *i915)
 static int ggtt_init_hw(struct i915_ggtt *ggtt)
 {
 	struct drm_i915_private *i915 = ggtt->vm.i915;
-	int ret = 0;
-
-	mutex_lock(&i915->drm.struct_mutex);
 
 	i915_address_space_init(&ggtt->vm, VM_CLASS_GGTT);
 
@@ -3203,18 +3203,14 @@ static int ggtt_init_hw(struct i915_ggtt *ggtt)
 				ggtt->gmadr.start,
 				ggtt->mappable_end)) {
 		ggtt->vm.cleanup(&ggtt->vm);
-		ret = -EIO;
-		goto out;
+		return -EIO;
 	}
 
 	ggtt->mtrr = arch_phys_wc_add(ggtt->gmadr.start, ggtt->mappable_end);
 
 	i915_ggtt_init_fences(ggtt);
 
-out:
-	mutex_unlock(&i915->drm.struct_mutex);
-
-	return ret;
+	return 0;
 }
 
 /**
@@ -3286,6 +3282,7 @@ static void ggtt_restore_mappings(struct i915_ggtt *ggtt)
 {
 	struct i915_vma *vma, *vn;
 	bool flush = false;
+	int open;
 
 	intel_gt_check_and_clear_faults(ggtt->vm.gt);
 
@@ -3293,7 +3290,9 @@ static void ggtt_restore_mappings(struct i915_ggtt *ggtt)
 
 	/* First fill our portion of the GTT with scratch pages */
 	ggtt->vm.clear_range(&ggtt->vm, 0, ggtt->vm.total);
-	ggtt->vm.closed = true; /* skip rewriting PTE on VMA unbind */
+
+	/* Skip rewriting PTE on VMA unbind. */
+	open = atomic_xchg(&ggtt->vm.open, 0);
 
 	/* clflush objects bound into the GGTT and rebind them. */
 	list_for_each_entry_safe(vma, vn, &ggtt->vm.bound_list, vm_link) {
@@ -3302,24 +3301,20 @@ static void ggtt_restore_mappings(struct i915_ggtt *ggtt)
 		if (!i915_vma_is_bound(vma, I915_VMA_GLOBAL_BIND))
 			continue;
 
-		mutex_unlock(&ggtt->vm.mutex);
-
 		if (!i915_vma_unbind(vma))
-			goto lock;
+			continue;
 
+		clear_bit(I915_VMA_GLOBAL_BIND_BIT, __i915_vma_flags(vma));
 		WARN_ON(i915_vma_bind(vma,
 				      obj ? obj->cache_level : 0,
-				      PIN_UPDATE));
+				      PIN_GLOBAL, NULL));
 		if (obj) { /* only used during resume => exclusive access */
 			flush |= fetch_and_zero(&obj->write_domain);
 			obj->read_domains |= I915_GEM_DOMAIN_GTT;
 		}
-
-lock:
-		mutex_lock(&ggtt->vm.mutex);
 	}
 
-	ggtt->vm.closed = false;
+	atomic_set(&ggtt->vm.open, open);
 	ggtt->invalidate(ggtt);
 
 	mutex_unlock(&ggtt->vm.mutex);
@@ -3711,7 +3706,8 @@ int i915_gem_gtt_insert(struct i915_address_space *vm,
 	u64 offset;
 	int err;
 
-	lockdep_assert_held(&vm->i915->drm.struct_mutex);
+	lockdep_assert_held(&vm->mutex);
+
 	GEM_BUG_ON(!size);
 	GEM_BUG_ON(!IS_ALIGNED(size, I915_GTT_PAGE_SIZE));
 	GEM_BUG_ON(alignment && !is_power_of_2(alignment));
diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.h b/drivers/gpu/drm/i915/i915_gem_gtt.h
index 007bdaf4ba00..e794d1f5eee8 100644
--- a/drivers/gpu/drm/i915/i915_gem_gtt.h
+++ b/drivers/gpu/drm/i915/i915_gem_gtt.h
@@ -307,7 +307,7 @@ struct i915_address_space {
 
 	unsigned int bind_alloc;
 
-	bool closed;
+	atomic_t open;
 
 	struct mutex mutex; /* protects vma and our lists */
 #define VM_CLASS_GGTT 0
@@ -575,6 +575,35 @@ static inline void i915_vm_put(struct i915_address_space *vm)
 	kref_put(&vm->ref, i915_vm_release);
 }
 
+static inline struct i915_address_space *
+i915_vm_open(struct i915_address_space *vm)
+{
+	GEM_BUG_ON(!atomic_read(&vm->open));
+	atomic_inc(&vm->open);
+	return i915_vm_get(vm);
+}
+
+static inline bool
+i915_vm_tryopen(struct i915_address_space *vm)
+{
+	if (atomic_add_unless(&vm->open, 1, 0))
+		return i915_vm_get(vm);
+
+	return false;
+}
+
+void __i915_vm_close(struct i915_address_space *vm);
+
+static inline void
+i915_vm_close(struct i915_address_space *vm)
+{
+	GEM_BUG_ON(!atomic_read(&vm->open));
+	if (atomic_dec_and_test(&vm->open))
+		__i915_vm_close(vm);
+
+	i915_vm_put(vm);
+}
+
 int gen6_ppgtt_pin(struct i915_ppgtt *base);
 void gen6_ppgtt_unpin(struct i915_ppgtt *base);
 void gen6_ppgtt_unpin_all(struct i915_ppgtt *base);
@@ -607,10 +636,9 @@ int i915_gem_gtt_insert(struct i915_address_space *vm,
 #define PIN_OFFSET_BIAS		BIT_ULL(6)
 #define PIN_OFFSET_FIXED	BIT_ULL(7)
 
-#define PIN_MBZ			BIT_ULL(8) /* I915_VMA_PIN_OVERFLOW */
-#define PIN_GLOBAL		BIT_ULL(9) /* I915_VMA_GLOBAL_BIND */
-#define PIN_USER		BIT_ULL(10) /* I915_VMA_LOCAL_BIND */
-#define PIN_UPDATE		BIT_ULL(11)
+#define PIN_UPDATE		BIT_ULL(9)
+#define PIN_GLOBAL		BIT_ULL(10) /* I915_VMA_GLOBAL_BIND */
+#define PIN_USER		BIT_ULL(11) /* I915_VMA_LOCAL_BIND */
 
 #define PIN_OFFSET_MASK		(-I915_GTT_PAGE_SIZE)
 
diff --git a/drivers/gpu/drm/i915/i915_perf.c b/drivers/gpu/drm/i915/i915_perf.c
index c1b764233761..06633b4ad260 100644
--- a/drivers/gpu/drm/i915/i915_perf.c
+++ b/drivers/gpu/drm/i915/i915_perf.c
@@ -1204,15 +1204,10 @@ static int i915_oa_read(struct i915_perf_stream *stream,
 static struct intel_context *oa_pin_context(struct i915_perf_stream *stream)
 {
 	struct i915_gem_engines_iter it;
-	struct drm_i915_private *i915 = stream->dev_priv;
 	struct i915_gem_context *ctx = stream->ctx;
 	struct intel_context *ce;
 	int err;
 
-	err = i915_mutex_lock_interruptible(&i915->drm);
-	if (err)
-		return ERR_PTR(err);
-
 	for_each_gem_engine(ce, i915_gem_context_lock_engines(ctx), it) {
 		if (ce->engine->class != RENDER_CLASS)
 			continue;
@@ -1229,10 +1224,6 @@ static struct intel_context *oa_pin_context(struct i915_perf_stream *stream)
 	}
 	i915_gem_context_unlock_engines(ctx);
 
-	mutex_unlock(&i915->drm.struct_mutex);
-	if (err)
-		return ERR_PTR(err);
-
 	return stream->pinned_ctx;
 }
 
@@ -1331,32 +1322,22 @@ static int oa_get_render_ctx_id(struct i915_perf_stream *stream)
  */
 static void oa_put_render_ctx_id(struct i915_perf_stream *stream)
 {
-	struct drm_i915_private *dev_priv = stream->dev_priv;
 	struct intel_context *ce;
 
 	stream->specific_ctx_id = INVALID_CTX_ID;
 	stream->specific_ctx_id_mask = 0;
 
 	ce = fetch_and_zero(&stream->pinned_ctx);
-	if (ce) {
-		mutex_lock(&dev_priv->drm.struct_mutex);
+	if (ce)
 		intel_context_unpin(ce);
-		mutex_unlock(&dev_priv->drm.struct_mutex);
-	}
 }
 
 static void
 free_oa_buffer(struct i915_perf_stream *stream)
 {
-	struct drm_i915_private *i915 = stream->dev_priv;
-
-	mutex_lock(&i915->drm.struct_mutex);
-
 	i915_vma_unpin_and_release(&stream->oa_buffer.vma,
 				   I915_VMA_RELEASE_MAP);
 
-	mutex_unlock(&i915->drm.struct_mutex);
-
 	stream->oa_buffer.vaddr = NULL;
 }
 
@@ -1511,18 +1492,13 @@ static int alloc_oa_buffer(struct i915_perf_stream *stream)
 	if (WARN_ON(stream->oa_buffer.vma))
 		return -ENODEV;
 
-	ret = i915_mutex_lock_interruptible(&dev_priv->drm);
-	if (ret)
-		return ret;
-
 	BUILD_BUG_ON_NOT_POWER_OF_2(OA_BUFFER_SIZE);
 	BUILD_BUG_ON(OA_BUFFER_SIZE < SZ_128K || OA_BUFFER_SIZE > SZ_16M);
 
 	bo = i915_gem_object_create_shmem(dev_priv, OA_BUFFER_SIZE);
 	if (IS_ERR(bo)) {
 		DRM_ERROR("Failed to allocate OA buffer\n");
-		ret = PTR_ERR(bo);
-		goto unlock;
+		return PTR_ERR(bo);
 	}
 
 	i915_gem_object_set_cache_coherency(bo, I915_CACHE_LLC);
@@ -1546,7 +1522,7 @@ static int alloc_oa_buffer(struct i915_perf_stream *stream)
 			 i915_ggtt_offset(stream->oa_buffer.vma),
 			 stream->oa_buffer.vaddr);
 
-	goto unlock;
+	return 0;
 
 err_unpin:
 	__i915_vma_unpin(vma);
@@ -1557,8 +1533,6 @@ static int alloc_oa_buffer(struct i915_perf_stream *stream)
 	stream->oa_buffer.vaddr = NULL;
 	stream->oa_buffer.vma = NULL;
 
-unlock:
-	mutex_unlock(&dev_priv->drm.struct_mutex);
 	return ret;
 }
 
diff --git a/drivers/gpu/drm/i915/i915_vma.c b/drivers/gpu/drm/i915/i915_vma.c
index 9adb85ba6daa..288679d8a6a9 100644
--- a/drivers/gpu/drm/i915/i915_vma.c
+++ b/drivers/gpu/drm/i915/i915_vma.c
@@ -32,6 +32,7 @@
 
 #include "i915_drv.h"
 #include "i915_globals.h"
+#include "i915_sw_fence_work.h"
 #include "i915_trace.h"
 #include "i915_vma.h"
 
@@ -110,7 +111,8 @@ vma_create(struct drm_i915_gem_object *obj,
 	if (vma == NULL)
 		return ERR_PTR(-ENOMEM);
 
-	vma->vm = vm;
+	mutex_init(&vma->pages_mutex);
+	vma->vm = i915_vm_get(vm);
 	vma->ops = &vm->vma_ops;
 	vma->obj = obj;
 	vma->resv = obj->base.resv;
@@ -261,8 +263,6 @@ vma_lookup(struct drm_i915_gem_object *obj,
  * Once created, the VMA is kept until either the object is freed, or the
  * address space is closed.
  *
- * Must be called with struct_mutex held.
- *
  * Returns the vma, or an error pointer.
  */
 struct i915_vma *
@@ -273,7 +273,7 @@ i915_vma_instance(struct drm_i915_gem_object *obj,
 	struct i915_vma *vma;
 
 	GEM_BUG_ON(view && !i915_is_ggtt(vm));
-	GEM_BUG_ON(vm->closed);
+	GEM_BUG_ON(!atomic_read(&vm->open));
 
 	spin_lock(&obj->vma.lock);
 	vma = vma_lookup(obj, vm, view);
@@ -287,18 +287,63 @@ i915_vma_instance(struct drm_i915_gem_object *obj,
 	return vma;
 }
 
+struct i915_vma_work {
+	struct dma_fence_work base;
+	struct i915_vma *vma;
+	enum i915_cache_level cache_level;
+	unsigned int flags;
+};
+
+static int __vma_bind(struct dma_fence_work *work)
+{
+	struct i915_vma_work *vw = container_of(work, typeof(*vw), base);
+	struct i915_vma *vma = vw->vma;
+	int err;
+
+	err = vma->ops->bind_vma(vma, vw->cache_level, vw->flags);
+	if (err)
+		atomic_or(I915_VMA_ERROR, &vma->flags);
+
+	if (vma->obj)
+		__i915_gem_object_unpin_pages(vma->obj);
+
+	return err;
+}
+
+static const struct dma_fence_work_ops bind_ops = {
+	.name = "bind",
+	.work = __vma_bind,
+};
+
+struct i915_vma_work *i915_vma_work(void)
+{
+	struct i915_vma_work *vw;
+
+	vw = kzalloc(sizeof(*vw), GFP_KERNEL);
+	if (!vw)
+		return NULL;
+
+	dma_fence_work_init(&vw->base, &bind_ops);
+	vw->base.dma.error = -EAGAIN; /* disable the worker by default */
+
+	return vw;
+}
+
 /**
  * i915_vma_bind - Sets up PTEs for an VMA in it's corresponding address space.
  * @vma: VMA to map
  * @cache_level: mapping cache level
  * @flags: flags like global or local mapping
+ * @work: preallocated worker for allocating and binding the PTE
  *
  * DMA addresses are taken from the scatter-gather table of this object (or of
  * this VMA in case of non-default GGTT views) and PTE entries set up.
  * Note that DMA addresses are also the only part of the SG table we care about.
  */
-int i915_vma_bind(struct i915_vma *vma, enum i915_cache_level cache_level,
-		  u32 flags)
+int i915_vma_bind(struct i915_vma *vma,
+		  enum i915_cache_level cache_level,
+		  u32 flags,
+		  struct i915_vma_work *work)
 {
 	u32 bind_flags;
 	u32 vma_flags;
@@ -315,11 +360,8 @@ int i915_vma_bind(struct i915_vma *vma, enum i915_cache_level cache_level,
 	if (GEM_DEBUG_WARN_ON(!flags))
 		return -EINVAL;
 
-	bind_flags = 0;
-	if (flags & PIN_GLOBAL)
-		bind_flags |= I915_VMA_GLOBAL_BIND;
-	if (flags & PIN_USER)
-		bind_flags |= I915_VMA_LOCAL_BIND;
+	bind_flags = flags;
+	bind_flags &= I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND;
 
 	vma_flags = atomic_read(&vma->flags);
 	vma_flags &= I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND;
@@ -333,9 +375,32 @@ int i915_vma_bind(struct i915_vma *vma, enum i915_cache_level cache_level,
 	GEM_BUG_ON(!vma->pages);
 
 	trace_i915_vma_bind(vma, bind_flags);
-	ret = vma->ops->bind_vma(vma, cache_level, bind_flags);
-	if (ret)
-		return ret;
+	if (work && (bind_flags & ~vma_flags) & vma->vm->bind_alloc) {
+		work->vma = vma;
+		work->cache_level = cache_level;
+		work->flags = bind_flags | I915_VMA_ALLOC;
+
+		/*
+		 * Note we only want to chain up to the migration fence on
+		 * the pages (not the object itself). As we don't track that,
+		 * yet, we have to use the exclusive fence instead.
+		 *
+		 * Also note that we do not want to track the async vma as
+		 * part of the obj->resv->excl_fence as it only affects
+		 * execution and not content or object's backing store lifetime.
+		 */
+		GEM_BUG_ON(i915_active_has_exclusive(&vma->active));
+		i915_active_set_exclusive(&vma->active, &work->base.dma);
+		work->base.dma.error = 0; /* enable the queue_work() */
+
+		if (vma->obj)
+			__i915_gem_object_pin_pages(vma->obj);
+	} else {
+		GEM_BUG_ON((bind_flags & ~vma_flags) & vma->vm->bind_alloc);
+		ret = vma->ops->bind_vma(vma, cache_level, bind_flags);
+		if (ret)
+			return ret;
+	}
 
 	atomic_or(bind_flags, &vma->flags);
 	return 0;
@@ -348,9 +413,7 @@ void __iomem *i915_vma_pin_iomap(struct i915_vma *vma)
 
 	/* Access through the GTT requires the device to be awake. */
 	assert_rpm_wakelock_held(&vma->vm->i915->runtime_pm);
-
-	lockdep_assert_held(&vma->vm->i915->drm.struct_mutex);
-	if (WARN_ON(!i915_vma_is_map_and_fenceable(vma))) {
+	if (GEM_WARN_ON(!i915_vma_is_map_and_fenceable(vma))) {
 		err = -ENODEV;
 		goto err;
 	}
@@ -368,7 +431,8 @@ void __iomem *i915_vma_pin_iomap(struct i915_vma *vma)
 			goto err;
 		}
 
-		vma->iomap = ptr;
+		if (unlikely(cmpxchg(&vma->iomap, NULL, ptr)))
+			io_mapping_unmap(ptr);
 	}
 
 	__i915_vma_pin(vma);
@@ -388,18 +452,12 @@ void __iomem *i915_vma_pin_iomap(struct i915_vma *vma)
 
 void i915_vma_flush_writes(struct i915_vma *vma)
 {
-	if (!i915_vma_has_ggtt_write(vma))
-		return;
-
-	intel_gt_flush_ggtt_writes(vma->vm->gt);
-
-	i915_vma_unset_ggtt_write(vma);
+	if (i915_vma_unset_ggtt_write(vma))
+		intel_gt_flush_ggtt_writes(vma->vm->gt);
 }
 
 void i915_vma_unpin_iomap(struct i915_vma *vma)
 {
-	lockdep_assert_held(&vma->vm->i915->drm.struct_mutex);
-
 	GEM_BUG_ON(vma->iomap == NULL);
 
 	i915_vma_flush_writes(vma);
@@ -435,6 +493,9 @@ bool i915_vma_misplaced(const struct i915_vma *vma,
 	if (!drm_mm_node_allocated(&vma->node))
 		return false;
 
+	if (test_bit(I915_VMA_ERROR_BIT, __i915_vma_flags(vma)))
+		return true;
+
 	if (vma->node.size < size)
 		return true;
 
@@ -538,7 +599,6 @@ static void assert_bind_count(const struct drm_i915_gem_object *obj)
 static int
 i915_vma_insert(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
 {
-	struct drm_i915_private *dev_priv = vma->vm->i915;
 	unsigned int cache_level;
 	u64 start, end;
 	int ret;
@@ -564,7 +624,7 @@ i915_vma_insert(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
 
 	end = vma->vm->total;
 	if (flags & PIN_MAPPABLE)
-		end = min_t(u64, end, dev_priv->ggtt.mappable_end);
+		end = min_t(u64, end, i915_vm_to_ggtt(vma->vm)->mappable_end);
 	if (flags & PIN_ZONE_4G)
 		end = min_t(u64, end, (1ULL << 32) - I915_GTT_PAGE_SIZE);
 	GEM_BUG_ON(!IS_ALIGNED(end, I915_GTT_PAGE_SIZE));
@@ -580,35 +640,21 @@ i915_vma_insert(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
 		return -ENOSPC;
 	}
 
-	if (vma->obj) {
-		ret = i915_gem_object_pin_pages(vma->obj);
-		if (ret)
-			return ret;
-
+	cache_level = 0;
+	if (vma->obj)
 		cache_level = vma->obj->cache_level;
-	} else {
-		cache_level = 0;
-	}
-
-	GEM_BUG_ON(vma->pages);
-
-	ret = vma->ops->set_pages(vma);
-	if (ret)
-		goto err_unpin;
 
 	if (flags & PIN_OFFSET_FIXED) {
 		u64 offset = flags & PIN_OFFSET_MASK;
 		if (!IS_ALIGNED(offset, alignment) ||
-		    range_overflows(offset, size, end)) {
-			ret = -EINVAL;
-			goto err_clear;
-		}
+		    range_overflows(offset, size, end))
+			return -EINVAL;
 
 		ret = i915_gem_gtt_reserve(vma->vm, &vma->node,
 					   size, offset, cache_level,
 					   flags);
 		if (ret)
-			goto err_clear;
+			return ret;
 	} else {
 		/*
 		 * We only support huge gtt pages through the 48b PPGTT,
@@ -647,7 +693,7 @@ i915_vma_insert(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
 					  size, alignment, cache_level,
 					  start, end, flags);
 		if (ret)
-			goto err_clear;
+			return ret;
 
 		GEM_BUG_ON(vma->node.start < start);
 		GEM_BUG_ON(vma->node.start + vma->node.size > end);
@@ -655,23 +701,15 @@ 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, cache_level));
 
-	mutex_lock(&vma->vm->mutex);
 	list_add_tail(&vma->vm_link, &vma->vm->bound_list);
-	mutex_unlock(&vma->vm->mutex);
 
 	if (vma->obj) {
+		atomic_inc(&vma->obj->mm.pages_pin_count);
 		atomic_inc(&vma->obj->bind_count);
 		assert_bind_count(vma->obj);
 	}
 
 	return 0;
-
-err_clear:
-	vma->ops->clear_pages(vma);
-err_unpin:
-	if (vma->obj)
-		i915_gem_object_unpin_pages(vma->obj);
-	return ret;
 }
 
 static void
@@ -680,12 +718,7 @@ i915_vma_remove(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));
 
-	vma->ops->clear_pages(vma);
-
-	mutex_lock(&vma->vm->mutex);
-	drm_mm_remove_node(&vma->node);
 	list_del(&vma->vm_link);
-	mutex_unlock(&vma->vm->mutex);
 
 	/*
 	 * Since the unbound list is global, only move to that list if
@@ -704,51 +737,206 @@ i915_vma_remove(struct i915_vma *vma)
 		i915_gem_object_unpin_pages(obj);
 		assert_bind_count(obj);
 	}
+
+	drm_mm_remove_node(&vma->node);
 }
 
-int __i915_vma_do_pin(struct i915_vma *vma,
-		      u64 size, u64 alignment, u64 flags)
+static bool try_fast_pin(struct i915_vma *vma, unsigned int flags)
 {
-	const unsigned int bound = atomic_read(&vma->flags);
-	int ret;
+	unsigned int bound;
+	bool pinned = true;
 
-	lockdep_assert_held(&vma->vm->i915->drm.struct_mutex);
-	GEM_BUG_ON((flags & (PIN_GLOBAL | PIN_USER)) == 0);
-	GEM_BUG_ON((flags & PIN_GLOBAL) && !i915_vma_is_ggtt(vma));
+	bound = atomic_read(&vma->flags);
+	do {
+		if (unlikely(flags & ~bound))
+			return false;
 
-	if (WARN_ON(bound & I915_VMA_PIN_OVERFLOW)) {
-		ret = -EBUSY;
-		goto err_unpin;
+		if (unlikely(bound & (I915_VMA_OVERFLOW | I915_VMA_ERROR)))
+			return false;
+
+		if (!(bound & I915_VMA_PIN_MASK))
+			goto slow;
+
+		GEM_BUG_ON(((bound + 1) & I915_VMA_PIN_MASK) == 0);
+	} while (!atomic_try_cmpxchg(&vma->flags, &bound, bound + 1));
+
+	return true;
+
+slow:
+	/*
+	 * If pin_count==0, but we are bound, check under the lock to avoid
+	 * racing with a concurrent i915_vma_unbind().
+	 */
+	mutex_lock(&vma->vm->mutex);
+	do {
+		if (unlikely(bound & (I915_VMA_OVERFLOW | I915_VMA_ERROR))) {
+			pinned = false;
+			break;
+		}
+
+		if (unlikely(flags & ~bound)) {
+			pinned = false;
+			break;
+		}
+	} while (!atomic_try_cmpxchg(&vma->flags, &bound, bound + 1));
+	mutex_unlock(&vma->vm->mutex);
+
+	return pinned;
+}
+
+static int vma_get_pages(struct i915_vma *vma)
+{
+	int err = 0;
+
+	if (atomic_add_unless(&vma->pages_count, 1, 0))
+		return 0;
+
+	if (mutex_lock_interruptible(&vma->pages_mutex))
+		return -EINTR;
+
+	if (!atomic_read(&vma->pages_count)) {
+		if (vma->obj) {
+			err = i915_gem_object_pin_pages(vma->obj);
+			if (err)
+				goto unlock;
+		}
+
+		err = vma->ops->set_pages(vma);
+		if (err)
+			goto unlock;
 	}
+	atomic_inc(&vma->pages_count);
 
-	if ((bound & I915_VMA_BIND_MASK) == 0) {
-		ret = i915_vma_insert(vma, size, alignment, flags);
-		if (ret)
-			goto err_unpin;
+unlock:
+	mutex_unlock(&vma->pages_mutex);
+
+	return err;
+}
+
+static void __vma_put_pages(struct i915_vma *vma, unsigned int count)
+{
+	mutex_lock_nested(&vma->pages_mutex, SINGLE_DEPTH_NESTING);
+	GEM_BUG_ON(atomic_read(&vma->pages_count) < count);
+	if (atomic_sub_return(count, &vma->pages_count) == 0) {
+		vma->ops->clear_pages(vma);
+		GEM_BUG_ON(vma->pages);
+		if (vma->obj)
+			i915_gem_object_unpin_pages(vma->obj);
 	}
-	GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
+	mutex_unlock(&vma->pages_mutex);
+}
 
-	ret = i915_vma_bind(vma, vma->obj ? vma->obj->cache_level : 0, flags);
-	if (ret)
-		goto err_remove;
+static void vma_put_pages(struct i915_vma *vma)
+{
+	if (atomic_add_unless(&vma->pages_count, -1, 1))
+		return;
 
-	GEM_BUG_ON(!i915_vma_is_bound(vma, I915_VMA_BIND_MASK));
+	__vma_put_pages(vma, 1);
+}
+
+static void vma_unbind_pages(struct i915_vma *vma)
+{
+	unsigned int count;
+
+	lockdep_assert_held(&vma->vm->mutex);
+
+	count = atomic_read(&vma->pages_count);
+	count >>= I915_VMA_PAGES_BIAS;
+	GEM_BUG_ON(!count);
+
+	__vma_put_pages(vma, count | count << I915_VMA_PAGES_BIAS);
+}
+
+int i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
+{
+	struct i915_vma_work *work = NULL;
+	unsigned int bound;
+	int err;
+
+	BUILD_BUG_ON(PIN_GLOBAL != I915_VMA_GLOBAL_BIND);
+	BUILD_BUG_ON(PIN_USER != I915_VMA_LOCAL_BIND);
+
+	GEM_BUG_ON(flags & PIN_UPDATE);
+	GEM_BUG_ON(!(flags & (PIN_USER | PIN_GLOBAL)));
 
-	if ((bound ^ atomic_read(&vma->flags)) & I915_VMA_GLOBAL_BIND)
-		__i915_vma_set_map_and_fenceable(vma);
+	if (try_fast_pin(vma, flags & I915_VMA_BIND_MASK))
+		return 0;
+
+	err = vma_get_pages(vma);
+	if (err)
+		return err;
+
+	if (flags & PIN_USER) {
+		work = i915_vma_work();
+		if (!work) {
+			err = -ENOMEM;
+			goto err_pages;
+		}
+	}
+
+	err = mutex_lock_interruptible(&vma->vm->mutex);
+	if (err)
+		goto err_fence;
+
+	bound = atomic_read(&vma->flags);
+	if (unlikely(bound & I915_VMA_ERROR)) {
+		err = -ENOMEM;
+		goto err_unlock;
+	}
 
+	if (unlikely(!((bound + 1) & I915_VMA_PIN_MASK))) {
+		err = -EAGAIN; /* pins are meant to be fairly temporary */
+		goto err_unlock;
+	}
+
+	if (unlikely(!(flags & ~bound & I915_VMA_BIND_MASK))) {
+		__i915_vma_pin(vma);
+		goto err_unlock;
+	}
+
+	err = i915_active_acquire(&vma->active);
+	if (err)
+		goto err_unlock;
+
+	if (!(bound & I915_VMA_BIND_MASK)) {
+		err = i915_vma_insert(vma, size, alignment, flags);
+		if (err)
+			goto err_active;
+
+		if (i915_is_ggtt(vma->vm))
+			__i915_vma_set_map_and_fenceable(vma);
+	}
+
+	GEM_BUG_ON(!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);
+
+	__i915_vma_pin(vma);
+	GEM_BUG_ON(!i915_vma_is_pinned(vma));
+	GEM_BUG_ON(!i915_vma_is_bound(vma, flags));
 	GEM_BUG_ON(i915_vma_misplaced(vma, size, alignment, flags));
-	return 0;
 
 err_remove:
-	if ((bound & I915_VMA_BIND_MASK) == 0) {
+	if (!i915_vma_is_bound(vma, I915_VMA_BIND_MASK))
 		i915_vma_remove(vma);
-		GEM_BUG_ON(vma->pages);
-		GEM_BUG_ON(atomic_read(&vma->flags) & I915_VMA_BIND_MASK);
-	}
-err_unpin:
-	__i915_vma_unpin(vma);
-	return ret;
+err_active:
+	i915_active_release(&vma->active);
+err_unlock:
+	mutex_unlock(&vma->vm->mutex);
+err_fence:
+	if (work)
+		dma_fence_work_commit(&work->base);
+err_pages:
+	vma_put_pages(vma);
+	return err;
 }
 
 void i915_vma_close(struct i915_vma *vma)
@@ -779,9 +967,6 @@ static void __i915_vma_remove_closed(struct i915_vma *vma)
 {
 	struct drm_i915_private *i915 = vma->vm->i915;
 
-	if (!i915_vma_is_closed(vma))
-		return;
-
 	spin_lock_irq(&i915->gt.closed_lock);
 	list_del_init(&vma->closed_link);
 	spin_unlock_irq(&i915->gt.closed_lock);
@@ -789,40 +974,35 @@ static void __i915_vma_remove_closed(struct i915_vma *vma)
 
 void i915_vma_reopen(struct i915_vma *vma)
 {
-	__i915_vma_remove_closed(vma);
+	if (i915_vma_is_closed(vma))
+		__i915_vma_remove_closed(vma);
 }
 
-static void __i915_vma_destroy(struct i915_vma *vma)
+void i915_vma_destroy(struct i915_vma *vma)
 {
-	GEM_BUG_ON(drm_mm_node_allocated(&vma->node));
-	GEM_BUG_ON(vma->fence);
+	if (drm_mm_node_allocated(&vma->node)) {
+		mutex_lock(&vma->vm->mutex);
+		atomic_and(~I915_VMA_PIN_MASK, &vma->flags);
+		WARN_ON(i915_vma_unbind(vma));
+		mutex_unlock(&vma->vm->mutex);
+		GEM_BUG_ON(drm_mm_node_allocated(&vma->node));
+	}
+	GEM_BUG_ON(i915_vma_is_active(vma));
 
 	if (vma->obj) {
 		struct drm_i915_gem_object *obj = vma->obj;
 
 		spin_lock(&obj->vma.lock);
 		list_del(&vma->obj_link);
-		rb_erase(&vma->obj_node, &vma->obj->vma.tree);
+		rb_erase(&vma->obj_node, &obj->vma.tree);
 		spin_unlock(&obj->vma.lock);
 	}
 
-	i915_active_fini(&vma->active);
-
-	i915_vma_free(vma);
-}
-
-void i915_vma_destroy(struct i915_vma *vma)
-{
-	lockdep_assert_held(&vma->vm->i915->drm.struct_mutex);
-
-	GEM_BUG_ON(i915_vma_is_pinned(vma));
-
 	__i915_vma_remove_closed(vma);
+	i915_vm_put(vma->vm);
 
-	WARN_ON(i915_vma_unbind(vma));
-	GEM_BUG_ON(i915_vma_is_active(vma));
-
-	__i915_vma_destroy(vma);
+	i915_active_fini(&vma->active);
+	i915_vma_free(vma);
 }
 
 void i915_vma_parked(struct drm_i915_private *i915)
@@ -831,12 +1011,32 @@ void i915_vma_parked(struct drm_i915_private *i915)
 
 	spin_lock_irq(&i915->gt.closed_lock);
 	list_for_each_entry_safe(vma, next, &i915->gt.closed_vma, closed_link) {
-		list_del_init(&vma->closed_link);
+		struct drm_i915_gem_object *obj = vma->obj;
+		struct i915_address_space *vm = vma->vm;
+
+		/* XXX All to avoid keeping a reference on i915_vma itself */
+
+		if (!kref_get_unless_zero(&obj->base.refcount))
+			continue;
+
+		if (!i915_vm_tryopen(vm)) {
+			i915_gem_object_put(obj);
+			obj = NULL;
+		}
+
 		spin_unlock_irq(&i915->gt.closed_lock);
 
-		i915_vma_destroy(vma);
+		if (obj) {
+			i915_vma_destroy(vma);
+			i915_gem_object_put(obj);
+		}
+
+		i915_vm_close(vm);
 
+		/* Restart after dropping lock */
 		spin_lock_irq(&i915->gt.closed_lock);
+		next = list_first_entry(&i915->gt.closed_vma,
+					typeof(*next), closed_link);
 	}
 	spin_unlock_irq(&i915->gt.closed_lock);
 }
@@ -887,6 +1087,10 @@ int i915_vma_move_to_active(struct i915_vma *vma,
 	assert_object_held(obj);
 	GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
 
+	err = i915_request_await_active(rq, &vma->active);
+	if (err)
+		return err;
+
 	/*
 	 * Add a reference if we're newly entering the active list.
 	 * The order in which we add operations to the retirement queue is
@@ -927,34 +1131,19 @@ int i915_vma_unbind(struct i915_vma *vma)
 {
 	int ret;
 
-	lockdep_assert_held(&vma->vm->i915->drm.struct_mutex);
+	lockdep_assert_held(&vma->vm->mutex);
 
 	/*
 	 * First wait upon any activity as retiring the request may
 	 * have side-effects such as unpinning or even unbinding this vma.
+	 *
+	 * XXX Actually waiting under the vm->mutex is a hinderance and
+	 * should be pipelined wherever possible. In cases where that is
+	 * unavoidable, we should lift the wait to before the mutex.
 	 */
-	might_sleep();
-	if (i915_vma_is_active(vma)) {
-		/*
-		 * When a closed VMA is retired, it is unbound - eek.
-		 * In order to prevent it from being recursively closed,
-		 * take a pin on the vma so that the second unbind is
-		 * aborted.
-		 *
-		 * Even more scary is that the retire callback may free
-		 * the object (last active vma). To prevent the explosion
-		 * we defer the actual object free to a worker that can
-		 * only proceed once it acquires the struct_mutex (which
-		 * we currently hold, therefore it cannot free this object
-		 * before we are finished).
-		 */
-		__i915_vma_pin(vma);
-		ret = i915_active_wait(&vma->active);
-		__i915_vma_unpin(vma);
-		if (ret)
-			return ret;
-	}
-	GEM_BUG_ON(i915_vma_is_active(vma));
+	ret = i915_active_wait(&vma->active);
+	if (ret)
+		return ret;
 
 	if (i915_vma_is_pinned(vma)) {
 		vma_print_allocator(vma, "is pinned");
@@ -975,16 +1164,12 @@ int i915_vma_unbind(struct i915_vma *vma)
 		GEM_BUG_ON(i915_vma_has_ggtt_write(vma));
 
 		/* release the fence reg _after_ flushing */
-		mutex_lock(&vma->vm->mutex);
 		ret = i915_vma_revoke_fence(vma);
-		mutex_unlock(&vma->vm->mutex);
 		if (ret)
 			return ret;
 
 		/* Force a pagefault for domain tracking on next user access */
-		mutex_lock(&vma->vm->mutex);
 		i915_vma_revoke_mmap(vma);
-		mutex_unlock(&vma->vm->mutex);
 
 		__i915_vma_iounmap(vma);
 		clear_bit(I915_VMA_CAN_FENCE_BIT, __i915_vma_flags(vma));
@@ -992,12 +1177,13 @@ int i915_vma_unbind(struct i915_vma *vma)
 	GEM_BUG_ON(vma->fence);
 	GEM_BUG_ON(i915_vma_has_userfault(vma));
 
-	if (likely(!vma->vm->closed)) {
+	if (likely(atomic_read(&vma->vm->open))) {
 		trace_i915_vma_unbind(vma);
 		vma->ops->unbind_vma(vma);
 	}
-	atomic_and(~I915_VMA_BIND_MASK, &vma->flags);
+	atomic_and(~(I915_VMA_BIND_MASK | I915_VMA_ERROR), &vma->flags);
 
+	vma_unbind_pages(vma);
 	i915_vma_remove(vma);
 
 	return 0;
diff --git a/drivers/gpu/drm/i915/i915_vma.h b/drivers/gpu/drm/i915/i915_vma.h
index 6053ce1dc95c..a784dc879168 100644
--- a/drivers/gpu/drm/i915/i915_vma.h
+++ b/drivers/gpu/drm/i915/i915_vma.h
@@ -96,25 +96,28 @@ struct i915_vma {
 	 * exclusive cachelines of a single page, so a maximum of 64 possible
 	 * users.
 	 */
-#define I915_VMA_PIN_MASK 0xff
-#define I915_VMA_PIN_OVERFLOW_BIT 8
-#define I915_VMA_PIN_OVERFLOW	((int)BIT(I915_VMA_PIN_OVERFLOW_BIT))
+#define I915_VMA_PIN_MASK 0x3ff
+#define I915_VMA_OVERFLOW 0x200
 
 	/** Flags and address space this VMA is bound to */
-#define I915_VMA_GLOBAL_BIND_BIT 9
-#define I915_VMA_LOCAL_BIND_BIT 10
+#define I915_VMA_GLOBAL_BIND_BIT 10
+#define I915_VMA_LOCAL_BIND_BIT  11
 
 #define I915_VMA_GLOBAL_BIND	((int)BIT(I915_VMA_GLOBAL_BIND_BIT))
 #define I915_VMA_LOCAL_BIND	((int)BIT(I915_VMA_LOCAL_BIND_BIT))
 
-#define I915_VMA_BIND_MASK (I915_VMA_GLOBAL_BIND | \
-			    I915_VMA_LOCAL_BIND | \
-			    I915_VMA_PIN_OVERFLOW)
+#define I915_VMA_BIND_MASK (I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND)
 
-#define I915_VMA_GGTT_BIT	11
-#define I915_VMA_CAN_FENCE_BIT	12
-#define I915_VMA_USERFAULT_BIT	13
-#define I915_VMA_GGTT_WRITE_BIT	14
+#define I915_VMA_ALLOC_BIT	12
+#define I915_VMA_ALLOC		((int)BIT(I915_VMA_ALLOC_BIT))
+
+#define I915_VMA_ERROR_BIT	13
+#define I915_VMA_ERROR		((int)BIT(I915_VMA_ERROR_BIT))
+
+#define I915_VMA_GGTT_BIT	14
+#define I915_VMA_CAN_FENCE_BIT	15
+#define I915_VMA_USERFAULT_BIT	16
+#define I915_VMA_GGTT_WRITE_BIT	17
 
 #define I915_VMA_GGTT		((int)BIT(I915_VMA_GGTT_BIT))
 #define I915_VMA_CAN_FENCE	((int)BIT(I915_VMA_CAN_FENCE_BIT))
@@ -123,6 +126,11 @@ 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.
@@ -307,8 +315,12 @@ i915_vma_compare(struct i915_vma *vma,
 	return memcmp(&vma->ggtt_view.partial, &view->partial, view->type);
 }
 
-int i915_vma_bind(struct i915_vma *vma, enum i915_cache_level cache_level,
-		  u32 flags);
+struct i915_vma_work *i915_vma_work(void);
+int i915_vma_bind(struct i915_vma *vma,
+		  enum i915_cache_level cache_level,
+		  u32 flags,
+		  struct i915_vma_work *work);
+
 bool i915_gem_valid_gtt_space(struct i915_vma *vma, unsigned long cache_level);
 bool i915_vma_misplaced(const struct i915_vma *vma,
 			u64 size, u64 alignment, u64 flags);
@@ -332,26 +344,8 @@ static inline void i915_vma_unlock(struct i915_vma *vma)
 	dma_resv_unlock(vma->resv);
 }
 
-int __i915_vma_do_pin(struct i915_vma *vma,
-		      u64 size, u64 alignment, u64 flags);
-static inline int __must_check
-i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
-{
-	BUILD_BUG_ON(PIN_MBZ != I915_VMA_PIN_OVERFLOW);
-	BUILD_BUG_ON(PIN_GLOBAL != I915_VMA_GLOBAL_BIND);
-	BUILD_BUG_ON(PIN_USER != I915_VMA_LOCAL_BIND);
-
-	/* Pin early to prevent the shrinker/eviction logic from destroying
-	 * our vma as we insert and bind.
-	 */
-	if (likely(((atomic_inc_return(&vma->flags) ^ flags) & I915_VMA_BIND_MASK) == 0)) {
-		GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
-		GEM_BUG_ON(i915_vma_misplaced(vma, size, alignment, flags));
-		return 0;
-	}
-
-	return __i915_vma_do_pin(vma, size, alignment, flags);
-}
+int __must_check
+i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags);
 
 static inline int i915_vma_pin_count(const struct i915_vma *vma)
 {
@@ -366,17 +360,17 @@ static inline bool i915_vma_is_pinned(const struct i915_vma *vma)
 static inline void __i915_vma_pin(struct i915_vma *vma)
 {
 	atomic_inc(&vma->flags);
-	GEM_BUG_ON(atomic_read(&vma->flags) & I915_VMA_PIN_OVERFLOW);
+	GEM_BUG_ON(!i915_vma_is_pinned(vma));
 }
 
 static inline void __i915_vma_unpin(struct i915_vma *vma)
 {
+	GEM_BUG_ON(!i915_vma_is_pinned(vma));
 	atomic_dec(&vma->flags);
 }
 
 static inline void i915_vma_unpin(struct i915_vma *vma)
 {
-	GEM_BUG_ON(!i915_vma_is_pinned(vma));
 	GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
 	__i915_vma_unpin(vma);
 }
@@ -396,8 +390,6 @@ static inline bool i915_vma_is_bound(const struct i915_vma *vma,
  * the caller must call i915_vma_unpin_iomap to relinquish the pinning
  * after the iomapping is no longer required.
  *
- * Callers must hold the struct_mutex.
- *
  * Returns a valid iomapped pointer or ERR_PTR.
  */
 void __iomem *i915_vma_pin_iomap(struct i915_vma *vma);
@@ -409,8 +401,8 @@ void __iomem *i915_vma_pin_iomap(struct i915_vma *vma);
  *
  * Unpins the previously iomapped VMA from i915_vma_pin_iomap().
  *
- * Callers must hold the struct_mutex. This function is only valid to be
- * called on a VMA previously iomapped by the caller with i915_vma_pin_iomap().
+ * This function is only valid to be called on a VMA previously
+ * iomapped by the caller with i915_vma_pin_iomap().
  */
 void i915_vma_unpin_iomap(struct i915_vma *vma);
 
@@ -438,6 +430,8 @@ static inline struct page *i915_vma_first_page(struct i915_vma *vma)
 int __must_check i915_vma_pin_fence(struct i915_vma *vma);
 int __must_check i915_vma_revoke_fence(struct i915_vma *vma);
 
+int __i915_vma_pin_fence(struct i915_vma *vma);
+
 static inline void __i915_vma_unpin_fence(struct i915_vma *vma)
 {
 	GEM_BUG_ON(atomic_read(&vma->fence->pin_count) <= 0);
@@ -455,7 +449,6 @@ static inline void __i915_vma_unpin_fence(struct i915_vma *vma)
 static inline void
 i915_vma_unpin_fence(struct i915_vma *vma)
 {
-	/* lockdep_assert_held(&vma->vm->i915->drm.struct_mutex); */
 	if (vma->fence)
 		__i915_vma_unpin_fence(vma);
 }
diff --git a/drivers/gpu/drm/i915/selftests/i915_gem_evict.c b/drivers/gpu/drm/i915/selftests/i915_gem_evict.c
index cb30c669b1b7..ba6064147173 100644
--- a/drivers/gpu/drm/i915/selftests/i915_gem_evict.c
+++ b/drivers/gpu/drm/i915/selftests/i915_gem_evict.c
@@ -106,14 +106,11 @@ static int populate_ggtt(struct drm_i915_private *i915,
 
 static void unpin_ggtt(struct drm_i915_private *i915)
 {
-	struct i915_ggtt *ggtt = &i915->ggtt;
 	struct i915_vma *vma;
 
-	mutex_lock(&ggtt->vm.mutex);
 	list_for_each_entry(vma, &i915->ggtt.vm.bound_list, vm_link)
 		if (vma->obj->mm.quirked)
 			i915_vma_unpin(vma);
-	mutex_unlock(&ggtt->vm.mutex);
 }
 
 static void cleanup_objects(struct drm_i915_private *i915,
@@ -127,11 +124,7 @@ static void cleanup_objects(struct drm_i915_private *i915,
 		i915_gem_object_put(obj);
 	}
 
-	mutex_unlock(&i915->drm.struct_mutex);
-
 	i915_gem_drain_freed_objects(i915);
-
-	mutex_lock(&i915->drm.struct_mutex);
 }
 
 static int igt_evict_something(void *arg)
@@ -148,10 +141,12 @@ static int igt_evict_something(void *arg)
 		goto cleanup;
 
 	/* Everything is pinned, nothing should happen */
+	mutex_lock(&ggtt->vm.mutex);
 	err = i915_gem_evict_something(&ggtt->vm,
 				       I915_GTT_PAGE_SIZE, 0, 0,
 				       0, U64_MAX,
 				       0);
+	mutex_unlock(&ggtt->vm.mutex);
 	if (err != -ENOSPC) {
 		pr_err("i915_gem_evict_something failed on a full GGTT with err=%d\n",
 		       err);
@@ -161,10 +156,12 @@ static int igt_evict_something(void *arg)
 	unpin_ggtt(i915);
 
 	/* Everything is unpinned, we should be able to evict something */
+	mutex_lock(&ggtt->vm.mutex);
 	err = i915_gem_evict_something(&ggtt->vm,
 				       I915_GTT_PAGE_SIZE, 0, 0,
 				       0, U64_MAX,
 				       0);
+	mutex_unlock(&ggtt->vm.mutex);
 	if (err) {
 		pr_err("i915_gem_evict_something failed on a full GGTT with err=%d\n",
 		       err);
@@ -230,7 +227,9 @@ static int igt_evict_for_vma(void *arg)
 		goto cleanup;
 
 	/* Everything is pinned, nothing should happen */
+	mutex_lock(&ggtt->vm.mutex);
 	err = i915_gem_evict_for_node(&ggtt->vm, &target, 0);
+	mutex_unlock(&ggtt->vm.mutex);
 	if (err != -ENOSPC) {
 		pr_err("i915_gem_evict_for_node on a full GGTT returned err=%d\n",
 		       err);
@@ -240,7 +239,9 @@ static int igt_evict_for_vma(void *arg)
 	unpin_ggtt(i915);
 
 	/* Everything is unpinned, we should be able to evict the node */
+	mutex_lock(&ggtt->vm.mutex);
 	err = i915_gem_evict_for_node(&ggtt->vm, &target, 0);
+	mutex_unlock(&ggtt->vm.mutex);
 	if (err) {
 		pr_err("i915_gem_evict_for_node returned err=%d\n",
 		       err);
@@ -317,7 +318,9 @@ static int igt_evict_for_cache_color(void *arg)
 	i915_vma_unpin(vma);
 
 	/* Remove just the second vma */
+	mutex_lock(&ggtt->vm.mutex);
 	err = i915_gem_evict_for_node(&ggtt->vm, &target, 0);
+	mutex_unlock(&ggtt->vm.mutex);
 	if (err) {
 		pr_err("[0]i915_gem_evict_for_node returned err=%d\n", err);
 		goto cleanup;
@@ -328,7 +331,9 @@ static int igt_evict_for_cache_color(void *arg)
 	 */
 	target.color = I915_CACHE_L3_LLC;
 
+	mutex_lock(&ggtt->vm.mutex);
 	err = i915_gem_evict_for_node(&ggtt->vm, &target, 0);
+	mutex_unlock(&ggtt->vm.mutex);
 	if (!err) {
 		pr_err("[1]i915_gem_evict_for_node returned err=%d\n", err);
 		err = -EINVAL;
@@ -358,7 +363,9 @@ static int igt_evict_vm(void *arg)
 		goto cleanup;
 
 	/* Everything is pinned, nothing should happen */
+	mutex_lock(&ggtt->vm.mutex);
 	err = i915_gem_evict_vm(&ggtt->vm);
+	mutex_unlock(&ggtt->vm.mutex);
 	if (err) {
 		pr_err("i915_gem_evict_vm on a full GGTT returned err=%d]\n",
 		       err);
@@ -367,7 +374,9 @@ static int igt_evict_vm(void *arg)
 
 	unpin_ggtt(i915);
 
+	mutex_lock(&ggtt->vm.mutex);
 	err = i915_gem_evict_vm(&ggtt->vm);
+	mutex_unlock(&ggtt->vm.mutex);
 	if (err) {
 		pr_err("i915_gem_evict_vm on a full GGTT returned err=%d]\n",
 		       err);
@@ -408,11 +417,11 @@ static int igt_evict_contexts(void *arg)
 	if (!HAS_FULL_PPGTT(i915))
 		return 0;
 
-	mutex_lock(&i915->drm.struct_mutex);
 	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
 
 	/* Reserve a block so that we know we have enough to fit a few rq */
 	memset(&hole, 0, sizeof(hole));
+	mutex_lock(&i915->ggtt.vm.mutex);
 	err = i915_gem_gtt_insert(&i915->ggtt.vm, &hole,
 				  PRETEND_GGTT_SIZE, 0, I915_COLOR_UNEVICTABLE,
 				  0, i915->ggtt.vm.total,
@@ -425,7 +434,9 @@ static int igt_evict_contexts(void *arg)
 	do {
 		struct reserved *r;
 
+		mutex_unlock(&i915->ggtt.vm.mutex);
 		r = kcalloc(1, sizeof(*r), GFP_KERNEL);
+		mutex_lock(&i915->ggtt.vm.mutex);
 		if (!r) {
 			err = -ENOMEM;
 			goto out_locked;
@@ -445,7 +456,7 @@ static int igt_evict_contexts(void *arg)
 		count++;
 	} while (1);
 	drm_mm_remove_node(&hole);
-	mutex_unlock(&i915->drm.struct_mutex);
+	mutex_unlock(&i915->ggtt.vm.mutex);
 	pr_info("Filled GGTT with %lu 1MiB nodes\n", count);
 
 	/* Overfill the GGTT with context objects and so try to evict one. */
@@ -508,7 +519,7 @@ static int igt_evict_contexts(void *arg)
 			break;
 	}
 
-	mutex_lock(&i915->drm.struct_mutex);
+	mutex_lock(&i915->ggtt.vm.mutex);
 out_locked:
 	if (igt_flush_test(i915, I915_WAIT_LOCKED))
 		err = -EIO;
@@ -522,8 +533,8 @@ static int igt_evict_contexts(void *arg)
 	}
 	if (drm_mm_node_allocated(&hole))
 		drm_mm_remove_node(&hole);
+	mutex_unlock(&i915->ggtt.vm.mutex);
 	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
-	mutex_unlock(&i915->drm.struct_mutex);
 
 	return err;
 }
@@ -545,12 +556,9 @@ int i915_gem_evict_mock_selftests(void)
 	if (!i915)
 		return -ENOMEM;
 
-	mutex_lock(&i915->drm.struct_mutex);
 	with_intel_runtime_pm(&i915->runtime_pm, wakeref)
 		err = i915_subtests(tests, i915);
 
-	mutex_unlock(&i915->drm.struct_mutex);
-
 	drm_dev_put(&i915->drm);
 	return err;
 }
diff --git a/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c b/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
index a90c9be95f8c..4235fa401956 100644
--- a/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
+++ b/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
@@ -35,16 +35,7 @@
 
 static void cleanup_freed_objects(struct drm_i915_private *i915)
 {
-	/*
-	 * As we may hold onto the struct_mutex for inordinate lengths of
-	 * time, the NMI khungtaskd detector may fire for the free objects
-	 * worker.
-	 */
-	mutex_unlock(&i915->drm.struct_mutex);
-
 	i915_gem_drain_freed_objects(i915);
-
-	mutex_lock(&i915->drm.struct_mutex);
 }
 
 static void fake_free_pages(struct drm_i915_gem_object *obj,
@@ -318,6 +309,17 @@ static int lowlevel_hole(struct drm_i915_private *i915,
 	return 0;
 }
 
+static int unlocked_vma_unbind(struct i915_vma *vma)
+{
+	int ret;
+
+	mutex_lock(&vma->vm->mutex);
+	ret = i915_vma_unbind(vma);
+	mutex_unlock(&vma->vm->mutex);
+
+	return ret;
+}
+
 static void close_object_list(struct list_head *objects,
 			      struct i915_address_space *vm)
 {
@@ -329,7 +331,7 @@ static void close_object_list(struct list_head *objects,
 
 		vma = i915_vma_instance(obj, vm, NULL);
 		if (!IS_ERR(vma))
-			ignored = i915_vma_unbind(vma);
+			ignored = unlocked_vma_unbind(vma);
 		/* Only ppgtt vma may be closed before the object is freed */
 		if (!IS_ERR(vma) && !i915_vma_is_ggtt(vma))
 			i915_vma_close(vma);
@@ -444,7 +446,7 @@ static int fill_hole(struct drm_i915_private *i915,
 						goto err;
 					}
 
-					err = i915_vma_unbind(vma);
+					err = unlocked_vma_unbind(vma);
 					if (err) {
 						pr_err("%s(%s) (forward) unbind of vma.node=%llx + %llx failed with err=%d\n",
 						       __func__, p->name, vma->node.start, vma->node.size,
@@ -517,7 +519,7 @@ static int fill_hole(struct drm_i915_private *i915,
 						goto err;
 					}
 
-					err = i915_vma_unbind(vma);
+					err = unlocked_vma_unbind(vma);
 					if (err) {
 						pr_err("%s(%s) (backward) unbind of vma.node=%llx + %llx failed with err=%d\n",
 						       __func__, p->name, vma->node.start, vma->node.size,
@@ -604,7 +606,7 @@ static int walk_hole(struct drm_i915_private *i915,
 				goto err_close;
 			}
 
-			err = i915_vma_unbind(vma);
+			err = unlocked_vma_unbind(vma);
 			if (err) {
 				pr_err("%s unbind failed at %llx + %llx  with err=%d\n",
 				       __func__, addr, vma->size, err);
@@ -685,13 +687,13 @@ static int pot_hole(struct drm_i915_private *i915,
 				pr_err("%s incorrect at %llx + %llx\n",
 				       __func__, addr, vma->size);
 				i915_vma_unpin(vma);
-				err = i915_vma_unbind(vma);
+				err = unlocked_vma_unbind(vma);
 				err = -EINVAL;
 				goto err;
 			}
 
 			i915_vma_unpin(vma);
-			err = i915_vma_unbind(vma);
+			err = unlocked_vma_unbind(vma);
 			GEM_BUG_ON(err);
 		}
 
@@ -789,13 +791,13 @@ static int drunk_hole(struct drm_i915_private *i915,
 				pr_err("%s incorrect at %llx + %llx\n",
 				       __func__, addr, BIT_ULL(size));
 				i915_vma_unpin(vma);
-				err = i915_vma_unbind(vma);
+				err = unlocked_vma_unbind(vma);
 				err = -EINVAL;
 				goto err;
 			}
 
 			i915_vma_unpin(vma);
-			err = i915_vma_unbind(vma);
+			err = unlocked_vma_unbind(vma);
 			GEM_BUG_ON(err);
 
 			if (igt_timeout(end_time,
@@ -867,7 +869,7 @@ static int __shrink_hole(struct drm_i915_private *i915,
 			pr_err("%s incorrect at %llx + %llx\n",
 			       __func__, addr, size);
 			i915_vma_unpin(vma);
-			err = i915_vma_unbind(vma);
+			err = unlocked_vma_unbind(vma);
 			err = -EINVAL;
 			break;
 		}
@@ -875,6 +877,15 @@ static int __shrink_hole(struct drm_i915_private *i915,
 		i915_vma_unpin(vma);
 		addr += size;
 
+		/*
+		 * Since we are injecting allocation faults at random intervals,
+		 * wait for this allocation to complete before we change the
+		 * faultinjection.
+		 */
+		err = i915_active_wait(&vma->active);
+		if (err)
+			break;
+
 		if (igt_timeout(end_time,
 				"%s timed out at ofset %llx [%llx - %llx]\n",
 				__func__, addr, hole_start, hole_end)) {
@@ -1008,21 +1019,19 @@ static int exercise_ppgtt(struct drm_i915_private *dev_priv,
 	if (IS_ERR(file))
 		return PTR_ERR(file);
 
-	mutex_lock(&dev_priv->drm.struct_mutex);
 	ppgtt = i915_ppgtt_create(dev_priv);
 	if (IS_ERR(ppgtt)) {
 		err = PTR_ERR(ppgtt);
-		goto out_unlock;
+		goto out_free;
 	}
 	GEM_BUG_ON(offset_in_page(ppgtt->vm.total));
-	GEM_BUG_ON(ppgtt->vm.closed);
+	GEM_BUG_ON(!atomic_read(&ppgtt->vm.open));
 
 	err = func(dev_priv, &ppgtt->vm, 0, ppgtt->vm.total, end_time);
 
 	i915_vm_put(&ppgtt->vm);
-out_unlock:
-	mutex_unlock(&dev_priv->drm.struct_mutex);
 
+out_free:
 	mock_file_free(dev_priv, file);
 	return err;
 }
@@ -1085,7 +1094,6 @@ static int exercise_ggtt(struct drm_i915_private *i915,
 	IGT_TIMEOUT(end_time);
 	int err = 0;
 
-	mutex_lock(&i915->drm.struct_mutex);
 restart:
 	list_sort(NULL, &ggtt->vm.mm.hole_stack, sort_holes);
 	drm_mm_for_each_hole(node, &ggtt->vm.mm, hole_start, hole_end) {
@@ -1106,7 +1114,6 @@ static int exercise_ggtt(struct drm_i915_private *i915,
 		last = hole_end;
 		goto restart;
 	}
-	mutex_unlock(&i915->drm.struct_mutex);
 
 	return err;
 }
@@ -1148,13 +1155,9 @@ static int igt_ggtt_page(void *arg)
 	unsigned int *order, n;
 	int err;
 
-	mutex_lock(&i915->drm.struct_mutex);
-
 	obj = i915_gem_object_create_internal(i915, PAGE_SIZE);
-	if (IS_ERR(obj)) {
-		err = PTR_ERR(obj);
-		goto out_unlock;
-	}
+	if (IS_ERR(obj))
+		return PTR_ERR(obj);
 
 	err = i915_gem_object_pin_pages(obj);
 	if (err)
@@ -1222,8 +1225,6 @@ static int igt_ggtt_page(void *arg)
 	i915_gem_object_unpin_pages(obj);
 out_free:
 	i915_gem_object_put(obj);
-out_unlock:
-	mutex_unlock(&i915->drm.struct_mutex);
 	return err;
 }
 
@@ -1234,6 +1235,9 @@ static void track_vma_bind(struct i915_vma *vma)
 	atomic_inc(&obj->bind_count); /* track for eviction later */
 	__i915_gem_object_pin_pages(obj);
 
+	GEM_BUG_ON(vma->pages);
+	atomic_set(&vma->pages_count, I915_VMA_PAGES_ACTIVE);
+	__i915_gem_object_pin_pages(obj);
 	vma->pages = obj->mm.pages;
 
 	mutex_lock(&vma->vm->mutex);
@@ -1330,11 +1334,13 @@ static int igt_gtt_reserve(void *arg)
 			goto out;
 		}
 
+		mutex_lock(&ggtt->vm.mutex);
 		err = i915_gem_gtt_reserve(&ggtt->vm, &vma->node,
 					   obj->base.size,
 					   total,
 					   obj->cache_level,
 					   0);
+		mutex_unlock(&ggtt->vm.mutex);
 		if (err) {
 			pr_err("i915_gem_gtt_reserve (pass 1) failed at %llu/%llu with err=%d\n",
 			       total, ggtt->vm.total, err);
@@ -1380,11 +1386,13 @@ static int igt_gtt_reserve(void *arg)
 			goto out;
 		}
 
+		mutex_lock(&ggtt->vm.mutex);
 		err = i915_gem_gtt_reserve(&ggtt->vm, &vma->node,
 					   obj->base.size,
 					   total,
 					   obj->cache_level,
 					   0);
+		mutex_unlock(&ggtt->vm.mutex);
 		if (err) {
 			pr_err("i915_gem_gtt_reserve (pass 2) failed at %llu/%llu with err=%d\n",
 			       total, ggtt->vm.total, err);
@@ -1414,7 +1422,7 @@ static int igt_gtt_reserve(void *arg)
 			goto out;
 		}
 
-		err = i915_vma_unbind(vma);
+		err = unlocked_vma_unbind(vma);
 		if (err) {
 			pr_err("i915_vma_unbind failed with err=%d!\n", err);
 			goto out;
@@ -1424,11 +1432,13 @@ static int igt_gtt_reserve(void *arg)
 				       2*I915_GTT_PAGE_SIZE,
 				       I915_GTT_MIN_ALIGNMENT);
 
+		mutex_lock(&ggtt->vm.mutex);
 		err = i915_gem_gtt_reserve(&ggtt->vm, &vma->node,
 					   obj->base.size,
 					   offset,
 					   obj->cache_level,
 					   0);
+		mutex_unlock(&ggtt->vm.mutex);
 		if (err) {
 			pr_err("i915_gem_gtt_reserve (pass 3) failed at %llu/%llu with err=%d\n",
 			       total, ggtt->vm.total, err);
@@ -1497,11 +1507,13 @@ static int igt_gtt_insert(void *arg)
 
 	/* Check a couple of obviously invalid requests */
 	for (ii = invalid_insert; ii->size; ii++) {
+		mutex_lock(&ggtt->vm.mutex);
 		err = i915_gem_gtt_insert(&ggtt->vm, &tmp,
 					  ii->size, ii->alignment,
 					  I915_COLOR_UNEVICTABLE,
 					  ii->start, ii->end,
 					  0);
+		mutex_unlock(&ggtt->vm.mutex);
 		if (err != -ENOSPC) {
 			pr_err("Invalid i915_gem_gtt_insert(.size=%llx, .alignment=%llx, .start=%llx, .end=%llx) succeeded (err=%d)\n",
 			       ii->size, ii->alignment, ii->start, ii->end,
@@ -1537,10 +1549,12 @@ static int igt_gtt_insert(void *arg)
 			goto out;
 		}
 
+		mutex_lock(&ggtt->vm.mutex);
 		err = i915_gem_gtt_insert(&ggtt->vm, &vma->node,
 					  obj->base.size, 0, obj->cache_level,
 					  0, ggtt->vm.total,
 					  0);
+		mutex_unlock(&ggtt->vm.mutex);
 		if (err == -ENOSPC) {
 			/* maxed out the GGTT space */
 			i915_gem_object_put(obj);
@@ -1589,16 +1603,18 @@ static int igt_gtt_insert(void *arg)
 		GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
 		offset = vma->node.start;
 
-		err = i915_vma_unbind(vma);
+		err = unlocked_vma_unbind(vma);
 		if (err) {
 			pr_err("i915_vma_unbind failed with err=%d!\n", err);
 			goto out;
 		}
 
+		mutex_lock(&ggtt->vm.mutex);
 		err = i915_gem_gtt_insert(&ggtt->vm, &vma->node,
 					  obj->base.size, 0, obj->cache_level,
 					  0, ggtt->vm.total,
 					  0);
+		mutex_unlock(&ggtt->vm.mutex);
 		if (err) {
 			pr_err("i915_gem_gtt_insert (pass 2) failed at %llu/%llu with err=%d\n",
 			       total, ggtt->vm.total, err);
@@ -1642,10 +1658,12 @@ static int igt_gtt_insert(void *arg)
 			goto out;
 		}
 
+		mutex_lock(&ggtt->vm.mutex);
 		err = i915_gem_gtt_insert(&ggtt->vm, &vma->node,
 					  obj->base.size, 0, obj->cache_level,
 					  0, ggtt->vm.total,
 					  0);
+		mutex_unlock(&ggtt->vm.mutex);
 		if (err) {
 			pr_err("i915_gem_gtt_insert (pass 3) failed at %llu/%llu with err=%d\n",
 			       total, ggtt->vm.total, err);
@@ -1689,8 +1707,9 @@ int i915_gem_gtt_mock_selftests(void)
 	}
 	mock_init_ggtt(i915, ggtt);
 
-	mutex_lock(&i915->drm.struct_mutex);
 	err = i915_subtests(tests, ggtt);
+
+	mutex_lock(&i915->drm.struct_mutex);
 	mock_device_flush(i915);
 	mutex_unlock(&i915->drm.struct_mutex);
 
diff --git a/drivers/gpu/drm/i915/selftests/i915_vma.c b/drivers/gpu/drm/i915/selftests/i915_vma.c
index 97752deecccb..e0ca12c17a7f 100644
--- a/drivers/gpu/drm/i915/selftests/i915_vma.c
+++ b/drivers/gpu/drm/i915/selftests/i915_vma.c
@@ -337,7 +337,9 @@ static int igt_vma_pin1(void *arg)
 
 		if (!err) {
 			i915_vma_unpin(vma);
+			mutex_lock(&ggtt->vm.mutex);
 			err = i915_vma_unbind(vma);
+			mutex_unlock(&ggtt->vm.mutex);
 			if (err) {
 				pr_err("Failed to unbind single page from GGTT, err=%d\n", err);
 				goto out;
@@ -831,8 +833,9 @@ int i915_vma_mock_selftests(void)
 	}
 	mock_init_ggtt(i915, ggtt);
 
-	mutex_lock(&i915->drm.struct_mutex);
 	err = i915_subtests(tests, ggtt);
+
+	mutex_lock(&i915->drm.struct_mutex);
 	mock_device_flush(i915);
 	mutex_unlock(&i915->drm.struct_mutex);
 
@@ -879,8 +882,6 @@ static int igt_vma_remapped_gtt(void *arg)
 	if (IS_ERR(obj))
 		return PTR_ERR(obj);
 
-	mutex_lock(&i915->drm.struct_mutex);
-
 	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
 
 	for (t = types; *t; t++) {
@@ -976,7 +977,6 @@ static int igt_vma_remapped_gtt(void *arg)
 
 out:
 	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
-	mutex_unlock(&i915->drm.struct_mutex);
 	i915_gem_object_put(obj);
 
 	return err;
-- 
2.23.0

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

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

* [PATCH 14/21] drm/i915: Push the i915_active.retire into a worker
  2019-09-02  4:02 [PATCH 01/21] drm/i915: Restrict the aliasing-ppgtt to the size of the ggtt Chris Wilson
                   ` (11 preceding siblings ...)
  2019-09-02  4:02 ` [PATCH 13/21] drm/i915: Pull i915_vma_pin under the vm->mutex Chris Wilson
@ 2019-09-02  4:02 ` Chris Wilson
  2019-09-02  4:02 ` [PATCH 15/21] drm/i915: Coordinate i915_active with its own mutex Chris Wilson
                   ` (10 subsequent siblings)
  23 siblings, 0 replies; 53+ messages in thread
From: Chris Wilson @ 2019-09-02  4:02 UTC (permalink / raw)
  To: intel-gfx; +Cc: Matthew Auld

As we need to use a mutex to serialise i915_active activation
(because we want to allow the callback to sleep), we need to push the
i915_active.retire into a worker callback in case we get need to retire
from an atomic context.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Reviewed-by: Matthew Auld <matthew.auld@intel.com>
---
 .../gpu/drm/i915/display/intel_frontbuffer.c  |  4 ++-
 drivers/gpu/drm/i915/gem/i915_gem_context.c   |  1 +
 drivers/gpu/drm/i915/gt/intel_context.c       |  2 ++
 drivers/gpu/drm/i915/gt/intel_engine_pool.c   |  1 +
 drivers/gpu/drm/i915/gt/intel_timeline.c      |  1 +
 drivers/gpu/drm/i915/i915_active.c            | 34 ++++++++++++++++---
 drivers/gpu/drm/i915/i915_active_types.h      | 13 ++++++-
 drivers/gpu/drm/i915/i915_vma.c               |  2 ++
 drivers/gpu/drm/i915/selftests/i915_active.c  |  6 ++--
 9 files changed, 55 insertions(+), 9 deletions(-)

diff --git a/drivers/gpu/drm/i915/display/intel_frontbuffer.c b/drivers/gpu/drm/i915/display/intel_frontbuffer.c
index fc40dc1fdbcc..6428b8dd70d3 100644
--- a/drivers/gpu/drm/i915/display/intel_frontbuffer.c
+++ b/drivers/gpu/drm/i915/display/intel_frontbuffer.c
@@ -206,6 +206,7 @@ static int frontbuffer_active(struct i915_active *ref)
 	return 0;
 }
 
+__i915_active_call
 static void frontbuffer_retire(struct i915_active *ref)
 {
 	struct intel_frontbuffer *front =
@@ -257,7 +258,8 @@ intel_frontbuffer_get(struct drm_i915_gem_object *obj)
 	kref_init(&front->ref);
 	atomic_set(&front->bits, 0);
 	i915_active_init(i915, &front->write,
-			 frontbuffer_active, frontbuffer_retire);
+			 frontbuffer_active,
+			 i915_active_may_sleep(frontbuffer_retire));
 
 	spin_lock(&i915->fb_tracking.lock);
 	if (obj->frontbuffer) {
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context.c b/drivers/gpu/drm/i915/gem/i915_gem_context.c
index 653f7275306a..4f303cb94698 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_context.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_context.c
@@ -848,6 +848,7 @@ struct context_barrier_task {
 	void *data;
 };
 
+__i915_active_call
 static void cb_retire(struct i915_active *base)
 {
 	struct context_barrier_task *cb = container_of(base, typeof(*cb), base);
diff --git a/drivers/gpu/drm/i915/gt/intel_context.c b/drivers/gpu/drm/i915/gt/intel_context.c
index c0495811f493..ae7c2689ef30 100644
--- a/drivers/gpu/drm/i915/gt/intel_context.c
+++ b/drivers/gpu/drm/i915/gt/intel_context.c
@@ -138,6 +138,7 @@ static void __context_unpin_state(struct i915_vma *vma)
 	__i915_vma_unpin(vma);
 }
 
+__i915_active_call
 static void __intel_context_retire(struct i915_active *active)
 {
 	struct intel_context *ce = container_of(active, typeof(*ce), active);
@@ -150,6 +151,7 @@ static void __intel_context_retire(struct i915_active *active)
 
 	intel_timeline_unpin(ce->timeline);
 	intel_ring_unpin(ce->ring);
+
 	intel_context_put(ce);
 }
 
diff --git a/drivers/gpu/drm/i915/gt/intel_engine_pool.c b/drivers/gpu/drm/i915/gt/intel_engine_pool.c
index 97d36cca8ded..81fab101fdb4 100644
--- a/drivers/gpu/drm/i915/gt/intel_engine_pool.c
+++ b/drivers/gpu/drm/i915/gt/intel_engine_pool.c
@@ -61,6 +61,7 @@ static int pool_active(struct i915_active *ref)
 	return 0;
 }
 
+__i915_active_call
 static void pool_retire(struct i915_active *ref)
 {
 	struct intel_engine_pool_node *node =
diff --git a/drivers/gpu/drm/i915/gt/intel_timeline.c b/drivers/gpu/drm/i915/gt/intel_timeline.c
index 9cb01d9828f1..d824bca43d55 100644
--- a/drivers/gpu/drm/i915/gt/intel_timeline.c
+++ b/drivers/gpu/drm/i915/gt/intel_timeline.c
@@ -136,6 +136,7 @@ static void __idle_cacheline_free(struct intel_timeline_cacheline *cl)
 	kfree(cl);
 }
 
+__i915_active_call
 static void __cacheline_retire(struct i915_active *active)
 {
 	struct intel_timeline_cacheline *cl =
diff --git a/drivers/gpu/drm/i915/i915_active.c b/drivers/gpu/drm/i915/i915_active.c
index 6a37ed52957a..f82d6d931824 100644
--- a/drivers/gpu/drm/i915/i915_active.c
+++ b/drivers/gpu/drm/i915/i915_active.c
@@ -132,6 +132,7 @@ __active_retire(struct i915_active *ref)
 	bool retire = false;
 
 	lockdep_assert_held(&ref->mutex);
+	GEM_BUG_ON(i915_active_is_idle(ref));
 
 	/* return the unused nodes to our slabcache -- flushing the allocator */
 	if (atomic_dec_and_test(&ref->count)) {
@@ -157,6 +158,19 @@ __active_retire(struct i915_active *ref)
 		ref->retire(ref);
 }
 
+static void
+active_work(struct work_struct *wrk)
+{
+	struct i915_active *ref = container_of(wrk, typeof(*ref), work);
+
+	GEM_BUG_ON(!atomic_read(&ref->count));
+	if (atomic_add_unless(&ref->count, -1, 1))
+		return;
+
+	mutex_lock(&ref->mutex);
+	__active_retire(ref);
+}
+
 static void
 active_retire(struct i915_active *ref)
 {
@@ -164,8 +178,13 @@ active_retire(struct i915_active *ref)
 	if (atomic_add_unless(&ref->count, -1, 1))
 		return;
 
-	/* One active may be flushed from inside the acquire of another */
-	mutex_lock_nested(&ref->mutex, SINGLE_DEPTH_NESTING);
+	/* If we are inside interrupt context (fence signaling), defer */
+	if (ref->flags & I915_ACTIVE_RETIRE_SLEEPS ||
+	    !mutex_trylock(&ref->mutex)) {
+		queue_work(system_unbound_wq, &ref->work);
+		return;
+	}
+
 	__active_retire(ref);
 }
 
@@ -240,12 +259,16 @@ void __i915_active_init(struct drm_i915_private *i915,
 			void (*retire)(struct i915_active *ref),
 			struct lock_class_key *key)
 {
+	unsigned long bits;
+
 	debug_active_init(ref);
 
 	ref->i915 = i915;
 	ref->flags = 0;
 	ref->active = active;
-	ref->retire = retire;
+	ref->retire = ptr_unpack_bits(retire, &bits, 2);
+	if (bits & I915_ACTIVE_MAY_SLEEP)
+		ref->flags |= I915_ACTIVE_RETIRE_SLEEPS;
 
 	ref->excl = NULL;
 	ref->tree = RB_ROOT;
@@ -253,6 +276,7 @@ void __i915_active_init(struct drm_i915_private *i915,
 	init_llist_head(&ref->preallocated_barriers);
 	atomic_set(&ref->count, 0);
 	__mutex_init(&ref->mutex, "i915_active", key);
+	INIT_WORK(&ref->work, active_work);
 }
 
 static bool ____active_del_barrier(struct i915_active *ref,
@@ -503,6 +527,7 @@ int i915_active_wait(struct i915_active *ref)
 	if (wait_on_bit(&ref->flags, I915_ACTIVE_GRAB_BIT, TASK_KILLABLE))
 		return -EINTR;
 
+	flush_work(&ref->work);
 	if (!i915_active_is_idle(ref))
 		return -EBUSY;
 
@@ -543,8 +568,9 @@ int i915_request_await_active(struct i915_request *rq, struct i915_active *ref)
 void i915_active_fini(struct i915_active *ref)
 {
 	debug_active_fini(ref);
-	GEM_BUG_ON(!RB_EMPTY_ROOT(&ref->tree));
 	GEM_BUG_ON(atomic_read(&ref->count));
+	GEM_BUG_ON(work_pending(&ref->work));
+	GEM_BUG_ON(!RB_EMPTY_ROOT(&ref->tree));
 	mutex_destroy(&ref->mutex);
 }
 #endif
diff --git a/drivers/gpu/drm/i915/i915_active_types.h b/drivers/gpu/drm/i915/i915_active_types.h
index 86e7a232ea3c..021167f0004d 100644
--- a/drivers/gpu/drm/i915/i915_active_types.h
+++ b/drivers/gpu/drm/i915/i915_active_types.h
@@ -13,6 +13,9 @@
 #include <linux/mutex.h>
 #include <linux/rbtree.h>
 #include <linux/rcupdate.h>
+#include <linux/workqueue.h>
+
+#include "i915_utils.h"
 
 struct drm_i915_private;
 struct i915_active_request;
@@ -44,6 +47,11 @@ struct i915_active_request {
 
 struct active_node;
 
+#define I915_ACTIVE_MAY_SLEEP BIT(0)
+
+#define __i915_active_call __aligned(4)
+#define i915_active_may_sleep(fn) ptr_pack_bits(&(fn), I915_ACTIVE_MAY_SLEEP, 2)
+
 struct i915_active {
 	struct drm_i915_private *i915;
 
@@ -57,11 +65,14 @@ struct i915_active {
 	struct dma_fence_cb excl_cb;
 
 	unsigned long flags;
-#define I915_ACTIVE_GRAB_BIT 0
+#define I915_ACTIVE_RETIRE_SLEEPS BIT(0)
+#define I915_ACTIVE_GRAB_BIT 1
 
 	int (*active)(struct i915_active *ref);
 	void (*retire)(struct i915_active *ref);
 
+	struct work_struct work;
+
 	struct llist_head preallocated_barriers;
 };
 
diff --git a/drivers/gpu/drm/i915/i915_vma.c b/drivers/gpu/drm/i915/i915_vma.c
index 288679d8a6a9..5ec8c970f9a2 100644
--- a/drivers/gpu/drm/i915/i915_vma.c
+++ b/drivers/gpu/drm/i915/i915_vma.c
@@ -91,6 +91,7 @@ static int __i915_vma_active(struct i915_active *ref)
 	return i915_vma_tryget(active_to_vma(ref)) ? 0 : -ENOENT;
 }
 
+__i915_active_call
 static void __i915_vma_retire(struct i915_active *ref)
 {
 	i915_vma_put(active_to_vma(ref));
@@ -1150,6 +1151,7 @@ int i915_vma_unbind(struct i915_vma *vma)
 		return -EBUSY;
 	}
 
+	GEM_BUG_ON(i915_vma_is_active(vma));
 	if (!drm_mm_node_allocated(&vma->node))
 		return 0;
 
diff --git a/drivers/gpu/drm/i915/selftests/i915_active.c b/drivers/gpu/drm/i915/selftests/i915_active.c
index 77d844ac8b71..d5ac9944d093 100644
--- a/drivers/gpu/drm/i915/selftests/i915_active.c
+++ b/drivers/gpu/drm/i915/selftests/i915_active.c
@@ -121,7 +121,7 @@ __live_active_setup(struct drm_i915_private *i915)
 	}
 
 	i915_active_release(&active->base);
-	if (active->retired && count) {
+	if (READ_ONCE(active->retired) && count) {
 		pr_err("i915_active retired before submission!\n");
 		err = -EINVAL;
 	}
@@ -161,7 +161,7 @@ static int live_active_wait(void *arg)
 	}
 
 	i915_active_wait(&active->base);
-	if (!active->retired) {
+	if (!READ_ONCE(active->retired)) {
 		pr_err("i915_active not retired after waiting!\n");
 		err = -EINVAL;
 	}
@@ -200,7 +200,7 @@ static int live_active_retire(void *arg)
 	if (igt_flush_test(i915, I915_WAIT_LOCKED))
 		err = -EIO;
 
-	if (!active->retired) {
+	if (!READ_ONCE(active->retired)) {
 		pr_err("i915_active not retired after flushing!\n");
 		err = -EINVAL;
 	}
-- 
2.23.0

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

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

* [PATCH 15/21] drm/i915: Coordinate i915_active with its own mutex
  2019-09-02  4:02 [PATCH 01/21] drm/i915: Restrict the aliasing-ppgtt to the size of the ggtt Chris Wilson
                   ` (12 preceding siblings ...)
  2019-09-02  4:02 ` [PATCH 14/21] drm/i915: Push the i915_active.retire into a worker Chris Wilson
@ 2019-09-02  4:02 ` Chris Wilson
  2019-09-20 16:14   ` Tvrtko Ursulin
  2019-09-02  4:02 ` [PATCH 16/21] drm/i915: Move idle barrier cleanup into engine-pm Chris Wilson
                   ` (9 subsequent siblings)
  23 siblings, 1 reply; 53+ messages in thread
From: Chris Wilson @ 2019-09-02  4:02 UTC (permalink / raw)
  To: intel-gfx

Forgo the struct_mutex serialisation for i915_active, and interpose its
own mutex handling for active/retire.

This is a multi-layered sleight-of-hand. First, we had to ensure that no
active/retire callbacks accidentally inverted the mutex ordering rules,
nor assumed that they were themselves serialised by struct_mutex. More
challenging though, is the rule over updating elements of the active
rbtree. Instead of the whole i915_active now being serialised by
struct_mutex, allocations/rotations of the tree are serialised by the
i915_active.mutex and individual nodes are serialised by the caller
using the i915_timeline.mutex (we need to use nested spinlocks to
interact with the dma_fence callback lists).

The pain point here is that instead of a single mutex around execbuf, we
now have to take a mutex for active tracker (one for each vma, context,
etc) and a couple of spinlocks for each fence update. The improvement in
fine grained locking allowing for multiple concurrent clients
(eventually!) should be worth it in typical loads.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 .../gpu/drm/i915/display/intel_frontbuffer.c  |   2 +-
 drivers/gpu/drm/i915/display/intel_overlay.c  |   5 +-
 .../gpu/drm/i915/gem/i915_gem_client_blt.c    |   2 +-
 drivers/gpu/drm/i915/gem/i915_gem_context.c   |   8 +-
 .../gpu/drm/i915/gem/i915_gem_object_types.h  |   1 +
 drivers/gpu/drm/i915/gem/i915_gem_pm.c        |   9 +-
 drivers/gpu/drm/i915/gt/intel_context.c       |   6 +-
 drivers/gpu/drm/i915/gt/intel_engine_pool.c   |   2 +-
 drivers/gpu/drm/i915/gt/intel_engine_pool.h   |   2 +-
 drivers/gpu/drm/i915/gt/intel_reset.c         |  10 +-
 drivers/gpu/drm/i915/gt/intel_timeline.c      |   9 +-
 .../gpu/drm/i915/gt/intel_timeline_types.h    |   2 +-
 drivers/gpu/drm/i915/gt/selftest_context.c    |  16 +-
 drivers/gpu/drm/i915/gt/selftest_lrc.c        |  10 +-
 .../gpu/drm/i915/gt/selftests/mock_timeline.c |   2 +-
 drivers/gpu/drm/i915/gvt/scheduler.c          |   3 -
 drivers/gpu/drm/i915/i915_active.c            | 291 +++++++---------
 drivers/gpu/drm/i915/i915_active.h            | 318 ++++--------------
 drivers/gpu/drm/i915/i915_active_types.h      |  23 +-
 drivers/gpu/drm/i915/i915_gem.c               |  42 ++-
 drivers/gpu/drm/i915/i915_gem_gtt.c           |   3 +-
 drivers/gpu/drm/i915/i915_gpu_error.c         |   4 +-
 drivers/gpu/drm/i915/i915_request.c           |  39 +--
 drivers/gpu/drm/i915/i915_request.h           |   1 -
 drivers/gpu/drm/i915/i915_vma.c               |   8 +-
 drivers/gpu/drm/i915/selftests/i915_active.c  |  36 +-
 26 files changed, 274 insertions(+), 580 deletions(-)

diff --git a/drivers/gpu/drm/i915/display/intel_frontbuffer.c b/drivers/gpu/drm/i915/display/intel_frontbuffer.c
index 6428b8dd70d3..84b164f31895 100644
--- a/drivers/gpu/drm/i915/display/intel_frontbuffer.c
+++ b/drivers/gpu/drm/i915/display/intel_frontbuffer.c
@@ -257,7 +257,7 @@ intel_frontbuffer_get(struct drm_i915_gem_object *obj)
 	front->obj = obj;
 	kref_init(&front->ref);
 	atomic_set(&front->bits, 0);
-	i915_active_init(i915, &front->write,
+	i915_active_init(&front->write,
 			 frontbuffer_active,
 			 i915_active_may_sleep(frontbuffer_retire));
 
diff --git a/drivers/gpu/drm/i915/display/intel_overlay.c b/drivers/gpu/drm/i915/display/intel_overlay.c
index 4f36557b3f3b..544e953342ea 100644
--- a/drivers/gpu/drm/i915/display/intel_overlay.c
+++ b/drivers/gpu/drm/i915/display/intel_overlay.c
@@ -230,7 +230,7 @@ alloc_request(struct intel_overlay *overlay, void (*fn)(struct intel_overlay *))
 	if (IS_ERR(rq))
 		return rq;
 
-	err = i915_active_ref(&overlay->last_flip, rq->timeline, rq);
+	err = i915_active_ref(&overlay->last_flip, rq->timeline, &rq->fence);
 	if (err) {
 		i915_request_add(rq);
 		return ERR_PTR(err);
@@ -1360,8 +1360,7 @@ void intel_overlay_setup(struct drm_i915_private *dev_priv)
 	overlay->contrast = 75;
 	overlay->saturation = 146;
 
-	i915_active_init(dev_priv,
-			 &overlay->last_flip,
+	i915_active_init(&overlay->last_flip,
 			 NULL, intel_overlay_last_flip_retire);
 
 	ret = get_registers(overlay, OVERLAY_NEEDS_PHYSICAL(dev_priv));
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 9e72b42a86f5..ace50bb9ee1f 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_client_blt.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_client_blt.c
@@ -160,7 +160,7 @@ static int move_to_active(struct i915_vma *vma, struct i915_request *rq)
 	if (err)
 		return err;
 
-	return i915_active_ref(&vma->active, rq->timeline, rq);
+	return i915_active_ref(&vma->active, rq->timeline, &rq->fence);
 }
 
 static void clear_pages_worker(struct work_struct *work)
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context.c b/drivers/gpu/drm/i915/gem/i915_gem_context.c
index 4f303cb94698..b8ddcf7899a1 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_context.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_context.c
@@ -868,20 +868,18 @@ static int context_barrier_task(struct i915_gem_context *ctx,
 				void (*task)(void *data),
 				void *data)
 {
-	struct drm_i915_private *i915 = ctx->i915;
 	struct context_barrier_task *cb;
 	struct i915_gem_engines_iter it;
 	struct intel_context *ce;
 	int err = 0;
 
-	lockdep_assert_held(&i915->drm.struct_mutex);
 	GEM_BUG_ON(!task);
 
 	cb = kmalloc(sizeof(*cb), GFP_KERNEL);
 	if (!cb)
 		return -ENOMEM;
 
-	i915_active_init(i915, &cb->base, NULL, cb_retire);
+	i915_active_init(&cb->base, NULL, cb_retire);
 	err = i915_active_acquire(&cb->base);
 	if (err) {
 		kfree(cb);
@@ -913,7 +911,9 @@ static int context_barrier_task(struct i915_gem_context *ctx,
 		if (emit)
 			err = emit(rq, data);
 		if (err == 0)
-			err = i915_active_ref(&cb->base, rq->timeline, rq);
+			err = i915_active_ref(&cb->base,
+					      rq->timeline,
+					      &rq->fence);
 
 		i915_request_add(rq);
 		if (err)
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 b0550727e69a..03e1e3206ab3 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_object_types.h
+++ b/drivers/gpu/drm/i915/gem/i915_gem_object_types.h
@@ -8,6 +8,7 @@
 #define __I915_GEM_OBJECT_TYPES_H__
 
 #include <drm/drm_gem.h>
+#include <uapi/drm/i915_drm.h>
 
 #include "i915_active.h"
 #include "i915_selftest.h"
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_pm.c b/drivers/gpu/drm/i915/gem/i915_gem_pm.c
index 92e53c25424c..92558fa47108 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_pm.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_pm.c
@@ -16,14 +16,11 @@ static void call_idle_barriers(struct intel_engine_cs *engine)
 	struct llist_node *node, *next;
 
 	llist_for_each_safe(node, next, llist_del_all(&engine->barrier_tasks)) {
-		struct i915_active_request *active =
+		struct dma_fence_cb *cb =
 			container_of((struct list_head *)node,
-				     typeof(*active), link);
+				     typeof(*cb), node);
 
-		INIT_LIST_HEAD(&active->link);
-		RCU_INIT_POINTER(active->request, NULL);
-
-		active->retire(active, NULL);
+		cb->func(NULL, cb);
 	}
 }
 
diff --git a/drivers/gpu/drm/i915/gt/intel_context.c b/drivers/gpu/drm/i915/gt/intel_context.c
index ae7c2689ef30..57e13c6f59c5 100644
--- a/drivers/gpu/drm/i915/gt/intel_context.c
+++ b/drivers/gpu/drm/i915/gt/intel_context.c
@@ -240,7 +240,7 @@ intel_context_init(struct intel_context *ce,
 
 	mutex_init(&ce->pin_mutex);
 
-	i915_active_init(ctx->i915, &ce->active,
+	i915_active_init(&ce->active,
 			 __intel_context_active, __intel_context_retire);
 }
 
@@ -307,7 +307,7 @@ int intel_context_prepare_remote_request(struct intel_context *ce,
 			return err;
 
 		/* Queue this switch after current activity by this context. */
-		err = i915_active_request_set(&tl->last_request, rq);
+		err = i915_active_fence_set(&tl->last_request, rq);
 		mutex_unlock(&tl->mutex);
 		if (err)
 			return err;
@@ -321,7 +321,7 @@ int intel_context_prepare_remote_request(struct intel_context *ce,
 	 * words transfer the pinned ce object to tracked active request.
 	 */
 	GEM_BUG_ON(i915_active_is_idle(&ce->active));
-	return i915_active_ref(&ce->active, rq->timeline, rq);
+	return i915_active_ref(&ce->active, rq->timeline, &rq->fence);
 }
 
 struct i915_request *intel_context_create_request(struct intel_context *ce)
diff --git a/drivers/gpu/drm/i915/gt/intel_engine_pool.c b/drivers/gpu/drm/i915/gt/intel_engine_pool.c
index 81fab101fdb4..3cdbd5f8b5be 100644
--- a/drivers/gpu/drm/i915/gt/intel_engine_pool.c
+++ b/drivers/gpu/drm/i915/gt/intel_engine_pool.c
@@ -95,7 +95,7 @@ node_create(struct intel_engine_pool *pool, size_t sz)
 		return ERR_PTR(-ENOMEM);
 
 	node->pool = pool;
-	i915_active_init(engine->i915, &node->active, pool_active, pool_retire);
+	i915_active_init(&node->active, pool_active, pool_retire);
 
 	obj = i915_gem_object_create_internal(engine->i915, sz);
 	if (IS_ERR(obj)) {
diff --git a/drivers/gpu/drm/i915/gt/intel_engine_pool.h b/drivers/gpu/drm/i915/gt/intel_engine_pool.h
index 7e2123b33594..4b0f3ae0c7e2 100644
--- a/drivers/gpu/drm/i915/gt/intel_engine_pool.h
+++ b/drivers/gpu/drm/i915/gt/intel_engine_pool.h
@@ -18,7 +18,7 @@ static inline int
 intel_engine_pool_mark_active(struct intel_engine_pool_node *node,
 			      struct i915_request *rq)
 {
-	return i915_active_ref(&node->active, rq->timeline, rq);
+	return i915_active_ref(&node->active, rq->timeline, &rq->fence);
 }
 
 static inline void
diff --git a/drivers/gpu/drm/i915/gt/intel_reset.c b/drivers/gpu/drm/i915/gt/intel_reset.c
index b9d84d52e986..4825c82aefee 100644
--- a/drivers/gpu/drm/i915/gt/intel_reset.c
+++ b/drivers/gpu/drm/i915/gt/intel_reset.c
@@ -814,10 +814,10 @@ static bool __intel_gt_unset_wedged(struct intel_gt *gt)
 	 */
 	spin_lock_irqsave(&timelines->lock, flags);
 	list_for_each_entry(tl, &timelines->active_list, link) {
-		struct i915_request *rq;
+		struct dma_fence *fence;
 
-		rq = i915_active_request_get_unlocked(&tl->last_request);
-		if (!rq)
+		fence = i915_active_fence_get(&tl->last_request);
+		if (!fence)
 			continue;
 
 		spin_unlock_irqrestore(&timelines->lock, flags);
@@ -829,8 +829,8 @@ static bool __intel_gt_unset_wedged(struct intel_gt *gt)
 		 * (I915_FENCE_TIMEOUT) so this wait should not be unbounded
 		 * in the worst case.
 		 */
-		dma_fence_default_wait(&rq->fence, false, MAX_SCHEDULE_TIMEOUT);
-		i915_request_put(rq);
+		dma_fence_default_wait(fence, false, MAX_SCHEDULE_TIMEOUT);
+		dma_fence_put(fence);
 
 		/* Restart iteration after droping lock */
 		spin_lock_irqsave(&timelines->lock, flags);
diff --git a/drivers/gpu/drm/i915/gt/intel_timeline.c b/drivers/gpu/drm/i915/gt/intel_timeline.c
index d824bca43d55..75d896167cfb 100644
--- a/drivers/gpu/drm/i915/gt/intel_timeline.c
+++ b/drivers/gpu/drm/i915/gt/intel_timeline.c
@@ -178,8 +178,7 @@ cacheline_alloc(struct intel_timeline_hwsp *hwsp, unsigned int cacheline)
 	cl->hwsp = hwsp;
 	cl->vaddr = page_pack_bits(vaddr, cacheline);
 
-	i915_active_init(hwsp->gt->i915, &cl->active,
-			 __cacheline_active, __cacheline_retire);
+	i915_active_init(&cl->active, __cacheline_active, __cacheline_retire);
 
 	return cl;
 }
@@ -255,7 +254,7 @@ int intel_timeline_init(struct intel_timeline *timeline,
 
 	mutex_init(&timeline->mutex);
 
-	INIT_ACTIVE_REQUEST(&timeline->last_request, &timeline->mutex);
+	INIT_ACTIVE_FENCE(&timeline->last_request, &timeline->mutex);
 	INIT_LIST_HEAD(&timeline->requests);
 
 	i915_syncmap_init(&timeline->sync);
@@ -443,7 +442,7 @@ __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);
+	err = i915_active_ref(&tl->hwsp_cacheline->active, tl, &rq->fence);
 	if (err)
 		goto err_cacheline;
 
@@ -494,7 +493,7 @@ int intel_timeline_get_seqno(struct intel_timeline *tl,
 static int cacheline_ref(struct intel_timeline_cacheline *cl,
 			 struct i915_request *rq)
 {
-	return i915_active_ref(&cl->active, rq->timeline, rq);
+	return i915_active_ref(&cl->active, rq->timeline, &rq->fence);
 }
 
 int intel_timeline_read_hwsp(struct i915_request *from,
diff --git a/drivers/gpu/drm/i915/gt/intel_timeline_types.h b/drivers/gpu/drm/i915/gt/intel_timeline_types.h
index 2b1baf2fcc8e..6d7ac129ce8a 100644
--- a/drivers/gpu/drm/i915/gt/intel_timeline_types.h
+++ b/drivers/gpu/drm/i915/gt/intel_timeline_types.h
@@ -63,7 +63,7 @@ struct intel_timeline {
 	 * the request using i915_active_request_get_request_rcu(), or hold the
 	 * struct_mutex.
 	 */
-	struct i915_active_request last_request;
+	struct i915_active_fence last_request;
 
 	/**
 	 * We track the most recent seqno that we wait on in every context so
diff --git a/drivers/gpu/drm/i915/gt/selftest_context.c b/drivers/gpu/drm/i915/gt/selftest_context.c
index 9d1ea26c7a2d..1420533e8fd5 100644
--- a/drivers/gpu/drm/i915/gt/selftest_context.c
+++ b/drivers/gpu/drm/i915/gt/selftest_context.c
@@ -41,24 +41,20 @@ static int context_sync(struct intel_context *ce)
 
 	mutex_lock(&tl->mutex);
 	do {
-		struct i915_request *rq;
+		struct dma_fence *fence;
 		long timeout;
 
-		rcu_read_lock();
-		rq = rcu_dereference(tl->last_request.request);
-		if (rq)
-			rq = i915_request_get_rcu(rq);
-		rcu_read_unlock();
-		if (!rq)
+		fence = i915_active_fence_get(&tl->last_request);
+		if (!fence)
 			break;
 
-		timeout = i915_request_wait(rq, 0, HZ / 10);
+		timeout = dma_fence_wait_timeout(fence, false, HZ / 10);
 		if (timeout < 0)
 			err = timeout;
 		else
-			i915_request_retire_upto(rq);
+			i915_request_retire_upto(to_request(fence));
 
-		i915_request_put(rq);
+		dma_fence_put(fence);
 	} while (!err);
 	mutex_unlock(&tl->mutex);
 
diff --git a/drivers/gpu/drm/i915/gt/selftest_lrc.c b/drivers/gpu/drm/i915/gt/selftest_lrc.c
index d791158988d6..aca1b3a9c5de 100644
--- a/drivers/gpu/drm/i915/gt/selftest_lrc.c
+++ b/drivers/gpu/drm/i915/gt/selftest_lrc.c
@@ -984,9 +984,13 @@ static struct i915_request *dummy_request(struct intel_engine_cs *engine)
 	if (!rq)
 		return NULL;
 
-	INIT_LIST_HEAD(&rq->active_list);
 	rq->engine = engine;
 
+	spin_lock_init(&rq->lock);
+	INIT_LIST_HEAD(&rq->fence.cb_list);
+	rq->fence.lock = &rq->lock;
+	rq->fence.ops = &i915_fence_ops;
+
 	i915_sched_node_init(&rq->sched);
 
 	/* mark this request as permanently incomplete */
@@ -1079,8 +1083,8 @@ static int live_suppress_wait_preempt(void *arg)
 				}
 
 				/* Disable NEWCLIENT promotion */
-				__i915_active_request_set(&rq[i]->timeline->last_request,
-							  dummy);
+				__i915_active_fence_set(&rq[i]->timeline->last_request,
+							&dummy->fence);
 				i915_request_add(rq[i]);
 			}
 
diff --git a/drivers/gpu/drm/i915/gt/selftests/mock_timeline.c b/drivers/gpu/drm/i915/gt/selftests/mock_timeline.c
index 598170efcaf6..2a77c051f36a 100644
--- a/drivers/gpu/drm/i915/gt/selftests/mock_timeline.c
+++ b/drivers/gpu/drm/i915/gt/selftests/mock_timeline.c
@@ -15,7 +15,7 @@ void mock_timeline_init(struct intel_timeline *timeline, u64 context)
 
 	mutex_init(&timeline->mutex);
 
-	INIT_ACTIVE_REQUEST(&timeline->last_request, &timeline->mutex);
+	INIT_ACTIVE_FENCE(&timeline->last_request, &timeline->mutex);
 	INIT_LIST_HEAD(&timeline->requests);
 
 	i915_syncmap_init(&timeline->sync);
diff --git a/drivers/gpu/drm/i915/gvt/scheduler.c b/drivers/gpu/drm/i915/gvt/scheduler.c
index 8940fa8d391a..6beb753b1ea1 100644
--- a/drivers/gpu/drm/i915/gvt/scheduler.c
+++ b/drivers/gpu/drm/i915/gvt/scheduler.c
@@ -385,11 +385,8 @@ intel_gvt_workload_req_alloc(struct intel_vgpu_workload *workload)
 {
 	struct intel_vgpu *vgpu = workload->vgpu;
 	struct intel_vgpu_submission *s = &vgpu->submission;
-	struct drm_i915_private *dev_priv = vgpu->gvt->dev_priv;
 	struct i915_request *rq;
 
-	lockdep_assert_held(&dev_priv->drm.struct_mutex);
-
 	if (workload->req)
 		return 0;
 
diff --git a/drivers/gpu/drm/i915/i915_active.c b/drivers/gpu/drm/i915/i915_active.c
index f82d6d931824..2e2ab8176620 100644
--- a/drivers/gpu/drm/i915/i915_active.c
+++ b/drivers/gpu/drm/i915/i915_active.c
@@ -12,8 +12,6 @@
 #include "i915_active.h"
 #include "i915_globals.h"
 
-#define BKL(ref) (&(ref)->i915->drm.struct_mutex)
-
 /*
  * Active refs memory management
  *
@@ -27,35 +25,35 @@ static struct i915_global_active {
 } global;
 
 struct active_node {
-	struct i915_active_request base;
+	struct i915_active_fence base;
 	struct i915_active *ref;
 	struct rb_node node;
 	u64 timeline;
 };
 
 static inline struct active_node *
-node_from_active(struct i915_active_request *active)
+node_from_active(struct i915_active_fence *active)
 {
 	return container_of(active, struct active_node, base);
 }
 
 #define take_preallocated_barriers(x) llist_del_all(&(x)->preallocated_barriers)
 
-static inline bool is_barrier(const struct i915_active_request *active)
+static inline bool is_barrier(const struct i915_active_fence *active)
 {
-	return IS_ERR(rcu_access_pointer(active->request));
+	return IS_ERR(rcu_access_pointer(active->fence));
 }
 
 static inline struct llist_node *barrier_to_ll(struct active_node *node)
 {
 	GEM_BUG_ON(!is_barrier(&node->base));
-	return (struct llist_node *)&node->base.link;
+	return (struct llist_node *)&node->base.cb.node;
 }
 
 static inline struct intel_engine_cs *
 __barrier_to_engine(struct active_node *node)
 {
-	return (struct intel_engine_cs *)READ_ONCE(node->base.link.prev);
+	return (struct intel_engine_cs *)READ_ONCE(node->base.cb.node.prev);
 }
 
 static inline struct intel_engine_cs *
@@ -68,7 +66,7 @@ barrier_to_engine(struct active_node *node)
 static inline struct active_node *barrier_from_ll(struct llist_node *x)
 {
 	return container_of((struct list_head *)x,
-			    struct active_node, base.link);
+			    struct active_node, base.cb.node);
 }
 
 #if IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM) && IS_ENABLED(CONFIG_DEBUG_OBJECTS)
@@ -147,15 +145,18 @@ __active_retire(struct i915_active *ref)
 	if (!retire)
 		return;
 
-	GEM_BUG_ON(rcu_access_pointer(ref->excl));
+	GEM_BUG_ON(rcu_access_pointer(ref->excl.fence));
 	rbtree_postorder_for_each_entry_safe(it, n, &root, node) {
-		GEM_BUG_ON(i915_active_request_isset(&it->base));
+		GEM_BUG_ON(i915_active_fence_isset(&it->base));
 		kmem_cache_free(global.slab_cache, it);
 	}
 
 	/* After the final retire, the entire struct may be freed */
 	if (ref->retire)
 		ref->retire(ref);
+
+	/* ... except if you wait on it, you must manage your own references! */
+	wake_up_var(ref);
 }
 
 static void
@@ -189,12 +190,20 @@ active_retire(struct i915_active *ref)
 }
 
 static void
-node_retire(struct i915_active_request *base, struct i915_request *rq)
+node_retire(struct dma_fence *fence, struct dma_fence_cb *cb)
 {
-	active_retire(node_from_active(base)->ref);
+	i915_active_fence_cb(fence, cb);
+	active_retire(container_of(cb, struct active_node, base.cb)->ref);
 }
 
-static struct i915_active_request *
+static void
+excl_retire(struct dma_fence *fence, struct dma_fence_cb *cb)
+{
+	i915_active_fence_cb(fence, cb);
+	active_retire(container_of(cb, struct i915_active, excl.cb));
+}
+
+static struct i915_active_fence *
 active_instance(struct i915_active *ref, struct intel_timeline *tl)
 {
 	struct active_node *node, *prealloc;
@@ -238,7 +247,7 @@ active_instance(struct i915_active *ref, struct intel_timeline *tl)
 	}
 
 	node = prealloc;
-	i915_active_request_init(&node->base, &tl->mutex, NULL, node_retire);
+	__i915_active_fence_init(&node->base, &tl->mutex, NULL, node_retire);
 	node->ref = ref;
 	node->timeline = idx;
 
@@ -253,8 +262,7 @@ active_instance(struct i915_active *ref, struct intel_timeline *tl)
 	return &node->base;
 }
 
-void __i915_active_init(struct drm_i915_private *i915,
-			struct i915_active *ref,
+void __i915_active_init(struct i915_active *ref,
 			int (*active)(struct i915_active *ref),
 			void (*retire)(struct i915_active *ref),
 			struct lock_class_key *key)
@@ -263,19 +271,18 @@ void __i915_active_init(struct drm_i915_private *i915,
 
 	debug_active_init(ref);
 
-	ref->i915 = i915;
 	ref->flags = 0;
 	ref->active = active;
 	ref->retire = ptr_unpack_bits(retire, &bits, 2);
 	if (bits & I915_ACTIVE_MAY_SLEEP)
 		ref->flags |= I915_ACTIVE_RETIRE_SLEEPS;
 
-	ref->excl = NULL;
 	ref->tree = RB_ROOT;
 	ref->cache = NULL;
 	init_llist_head(&ref->preallocated_barriers);
 	atomic_set(&ref->count, 0);
 	__mutex_init(&ref->mutex, "i915_active", key);
+	__i915_active_fence_init(&ref->excl, &ref->mutex, NULL, excl_retire);
 	INIT_WORK(&ref->work, active_work);
 }
 
@@ -329,9 +336,9 @@ __active_del_barrier(struct i915_active *ref, struct active_node *node)
 
 int i915_active_ref(struct i915_active *ref,
 		    struct intel_timeline *tl,
-		    struct i915_request *rq)
+		    struct dma_fence *fence)
 {
-	struct i915_active_request *active;
+	struct i915_active_fence *active;
 	int err;
 
 	lockdep_assert_held(&tl->mutex);
@@ -354,65 +361,38 @@ int i915_active_ref(struct i915_active *ref,
 		 * request that we want to emit on the kernel_context.
 		 */
 		__active_del_barrier(ref, node_from_active(active));
-		RCU_INIT_POINTER(active->request, NULL);
-		INIT_LIST_HEAD(&active->link);
-	} else {
-		if (!i915_active_request_isset(active))
-			atomic_inc(&ref->count);
+		RCU_INIT_POINTER(active->fence, NULL);
+		atomic_dec(&ref->count);
 	}
-	GEM_BUG_ON(!atomic_read(&ref->count));
-	__i915_active_request_set(active, rq);
+	if (!__i915_active_fence_set(active, fence))
+		atomic_inc(&ref->count);
 
 out:
 	i915_active_release(ref);
 	return err;
 }
 
-static void excl_cb(struct dma_fence *f, struct dma_fence_cb *cb)
-{
-	struct i915_active *ref = container_of(cb, typeof(*ref), excl_cb);
-
-	RCU_INIT_POINTER(ref->excl, NULL);
-	dma_fence_put(f);
-
-	active_retire(ref);
-}
-
 void i915_active_set_exclusive(struct i915_active *ref, struct dma_fence *f)
 {
 	GEM_BUG_ON(i915_active_is_idle(ref));
 
-	dma_fence_get(f);
-
-	rcu_read_lock();
-	if (rcu_access_pointer(ref->excl)) {
-		struct dma_fence *old;
-
-		old = dma_fence_get_rcu_safe(&ref->excl);
-		if (old) {
-			if (dma_fence_remove_callback(old, &ref->excl_cb))
-				atomic_dec(&ref->count);
-			dma_fence_put(old);
-		}
-	}
-	rcu_read_unlock();
-
-	atomic_inc(&ref->count);
-	rcu_assign_pointer(ref->excl, f);
+	mutex_acquire(&ref->mutex.dep_map, 0, 0, _THIS_IP_);
+	if (!__i915_active_fence_set(&ref->excl, f))
+		atomic_inc(&ref->count);
+	mutex_release(&ref->mutex.dep_map, 0, _THIS_IP_);
+}
 
-	if (dma_fence_add_callback(f, &ref->excl_cb, excl_cb)) {
-		RCU_INIT_POINTER(ref->excl, NULL);
-		atomic_dec(&ref->count);
-		dma_fence_put(f);
-	}
+bool i915_active_acquire_if_busy(struct i915_active *ref)
+{
+	debug_active_assert(ref);
+	return atomic_add_unless(&ref->count, 1, 0);
 }
 
 int i915_active_acquire(struct i915_active *ref)
 {
 	int err;
 
-	debug_active_assert(ref);
-	if (atomic_add_unless(&ref->count, 1, 0))
+	if (i915_active_acquire_if_busy(ref))
 		return 0;
 
 	err = mutex_lock_interruptible(&ref->mutex);
@@ -437,121 +417,49 @@ void i915_active_release(struct i915_active *ref)
 	active_retire(ref);
 }
 
-static void __active_ungrab(struct i915_active *ref)
-{
-	clear_and_wake_up_bit(I915_ACTIVE_GRAB_BIT, &ref->flags);
-}
-
-bool i915_active_trygrab(struct i915_active *ref)
-{
-	debug_active_assert(ref);
-
-	if (test_and_set_bit(I915_ACTIVE_GRAB_BIT, &ref->flags))
-		return false;
-
-	if (!atomic_add_unless(&ref->count, 1, 0)) {
-		__active_ungrab(ref);
-		return false;
-	}
-
-	return true;
-}
-
-void i915_active_ungrab(struct i915_active *ref)
-{
-	GEM_BUG_ON(!test_bit(I915_ACTIVE_GRAB_BIT, &ref->flags));
-
-	active_retire(ref);
-	__active_ungrab(ref);
-}
-
-static int excl_wait(struct i915_active *ref)
-{
-	struct dma_fence *old;
-	int err = 0;
-
-	if (!rcu_access_pointer(ref->excl))
-		return 0;
-
-	rcu_read_lock();
-	old = dma_fence_get_rcu_safe(&ref->excl);
-	rcu_read_unlock();
-	if (old) {
-		err = dma_fence_wait(old, true);
-		dma_fence_put(old);
-	}
-
-	return err;
-}
-
 int i915_active_wait(struct i915_active *ref)
 {
 	struct active_node *it, *n;
-	int err;
+	int err = 0;
 
 	might_sleep();
-	might_lock(&ref->mutex);
 
-	if (i915_active_is_idle(ref))
+	if (!i915_active_acquire_if_busy(ref))
 		return 0;
 
-	err = mutex_lock_interruptible(&ref->mutex);
-	if (err)
-		return err;
-
-	if (!atomic_add_unless(&ref->count, 1, 0)) {
-		mutex_unlock(&ref->mutex);
-		return 0;
-	}
+	/* Flush lazy signals */
+	rbtree_postorder_for_each_entry_safe(it, n, &ref->tree, node) {
+		struct dma_fence *fence;
 
-	err = excl_wait(ref);
-	if (err)
-		goto out;
+		if (is_barrier(&it->base)) /* unconnected idle barrier */
+			continue;
 
-	rbtree_postorder_for_each_entry_safe(it, n, &ref->tree, node) {
-		if (is_barrier(&it->base)) { /* unconnected idle-barrier */
-			err = -EBUSY;
-			break;
+		fence = i915_active_fence_get(&it->base);
+		if (fence) {
+			dma_fence_enable_sw_signaling(fence);
+			dma_fence_put(fence);
 		}
-
-		err = i915_active_request_retire(&it->base, BKL(ref));
-		if (err)
-			break;
 	}
-
-out:
-	__active_retire(ref);
+	/* Any fence added after the wait begins will not be auto-signaled */
+	i915_active_release(ref);
 	if (err)
 		return err;
 
-	if (wait_on_bit(&ref->flags, I915_ACTIVE_GRAB_BIT, TASK_KILLABLE))
+	if (wait_var_event_interruptible(ref, i915_active_is_idle(ref)))
 		return -EINTR;
 
-	flush_work(&ref->work);
-	if (!i915_active_is_idle(ref))
-		return -EBUSY;
-
 	return 0;
 }
 
-int i915_request_await_active_request(struct i915_request *rq,
-				      struct i915_active_request *active)
-{
-	struct i915_request *barrier =
-		i915_active_request_raw(active, &rq->i915->drm.struct_mutex);
-
-	return barrier ? i915_request_await_dma_fence(rq, &barrier->fence) : 0;
-}
-
 int i915_request_await_active(struct i915_request *rq, struct i915_active *ref)
 {
 	int err = 0;
 
-	if (rcu_access_pointer(ref->excl)) {
+	if (rcu_access_pointer(ref->excl.fence)) {
 		struct dma_fence *fence;
 
 		rcu_read_lock();
-		fence = dma_fence_get_rcu_safe(&ref->excl);
+		fence = dma_fence_get_rcu_safe(&ref->excl.fence);
 		rcu_read_unlock();
 		if (fence) {
 			err = i915_request_await_dma_fence(rq, fence);
@@ -577,7 +485,7 @@ void i915_active_fini(struct i915_active *ref)
 
 static inline bool is_idle_barrier(struct active_node *node, u64 idx)
 {
-	return node->timeline == idx && !i915_active_request_isset(&node->base);
+	return node->timeline == idx && !i915_active_fence_isset(&node->base);
 }
 
 static struct active_node *reuse_idle_barrier(struct i915_active *ref, u64 idx)
@@ -697,13 +605,13 @@ int i915_active_acquire_preallocate_barrier(struct i915_active *ref,
 			node->base.lock =
 				&engine->kernel_context->timeline->mutex;
 #endif
-			RCU_INIT_POINTER(node->base.request, NULL);
-			node->base.retire = node_retire;
+			RCU_INIT_POINTER(node->base.fence, NULL);
+			node->base.cb.func = node_retire;
 			node->timeline = idx;
 			node->ref = ref;
 		}
 
-		if (!i915_active_request_isset(&node->base)) {
+		if (!i915_active_fence_isset(&node->base)) {
 			/*
 			 * Mark this as being *our* unconnected proto-node.
 			 *
@@ -713,8 +621,8 @@ int i915_active_acquire_preallocate_barrier(struct i915_active *ref,
 			 * and then we can use the rb_node and list pointers
 			 * for our tracking of the pending barrier.
 			 */
-			RCU_INIT_POINTER(node->base.request, ERR_PTR(-EAGAIN));
-			node->base.link.prev = (void *)engine;
+			RCU_INIT_POINTER(node->base.fence, ERR_PTR(-EAGAIN));
+			node->base.cb.node.prev = (void *)engine;
 			atomic_inc(&ref->count);
 		}
 
@@ -781,25 +689,65 @@ void i915_request_add_active_barriers(struct i915_request *rq)
 {
 	struct intel_engine_cs *engine = rq->engine;
 	struct llist_node *node, *next;
+	unsigned long flags;
 
 	GEM_BUG_ON(intel_engine_is_virtual(engine));
 	GEM_BUG_ON(rq->timeline != engine->kernel_context->timeline);
 
+	node = llist_del_all(&engine->barrier_tasks);
+	if (!node)
+		return;
 	/*
 	 * Attach the list of proto-fences to the in-flight request such
 	 * that the parent i915_active will be released when this request
 	 * is retired.
 	 */
-	llist_for_each_safe(node, next, llist_del_all(&engine->barrier_tasks)) {
-		RCU_INIT_POINTER(barrier_from_ll(node)->base.request, rq);
+	spin_lock_irqsave(&rq->lock, flags);
+	llist_for_each_safe(node, next, node) {
+		RCU_INIT_POINTER(barrier_from_ll(node)->base.fence, &rq->fence);
 		smp_wmb(); /* serialise with reuse_idle_barrier */
-		list_add_tail((struct list_head *)node, &rq->active_list);
+		list_add_tail((struct list_head *)node, &rq->fence.cb_list);
+	}
+	spin_unlock_irqrestore(&rq->lock, flags);
+}
+
+#if IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM)
+#define active_is_held(active) lockdep_is_held((active)->lock)
+#else
+#define active_is_held(active) true
+#endif
+
+struct dma_fence *
+__i915_active_fence_set(struct i915_active_fence *active,
+			struct dma_fence *fence)
+{
+	struct dma_fence *prev;
+	unsigned long flags;
+
+	/* NB: updates must be serialised by an outer timeline mutex */
+	spin_lock_irqsave(fence->lock, flags);
+	GEM_BUG_ON(test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags));
+
+	prev = rcu_dereference_protected(active->fence, active_is_held(active));
+	if (prev) {
+		spin_lock_nested(prev->lock, SINGLE_DEPTH_NESTING);
+		__list_del_entry(&active->cb.node);
+		spin_unlock(prev->lock); /* serialise with prev->cb_list */
+		prev = rcu_access_pointer(active->fence);
 	}
+
+	rcu_assign_pointer(active->fence, fence);
+	list_add_tail(&active->cb.node, &fence->cb_list);
+
+	spin_unlock_irqrestore(fence->lock, flags);
+
+	return prev;
 }
 
-int i915_active_request_set(struct i915_active_request *active,
-			    struct i915_request *rq)
+int i915_active_fence_set(struct i915_active_fence *active,
+			  struct i915_request *rq)
 {
+	struct dma_fence *fence;
 	int err;
 
 #if IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM)
@@ -807,18 +755,25 @@ int i915_active_request_set(struct i915_active_request *active,
 #endif
 
 	/* Must maintain ordering wrt previous active requests */
-	err = i915_request_await_active_request(rq, active);
-	if (err)
-		return err;
+	rcu_read_lock();
+	fence = __i915_active_fence_set(active, &rq->fence);
+	if (fence)
+		fence = dma_fence_get_rcu(fence);
+	rcu_read_unlock();
+
+	if (fence) {
+		err = i915_request_await_dma_fence(rq, fence);
+		dma_fence_put(fence);
+		if (err)
+			return err;
+	}
 
-	__i915_active_request_set(active, rq);
 	return 0;
 }
 
-void i915_active_retire_noop(struct i915_active_request *active,
-			     struct i915_request *request)
+void i915_active_noop(struct dma_fence *fence, struct dma_fence_cb *cb)
 {
-	/* Space left intentionally blank */
+	i915_active_fence_cb(fence, cb);
 }
 
 #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
diff --git a/drivers/gpu/drm/i915/i915_active.h b/drivers/gpu/drm/i915/i915_active.h
index af3d536e26fd..e1912d539014 100644
--- a/drivers/gpu/drm/i915/i915_active.h
+++ b/drivers/gpu/drm/i915/i915_active.h
@@ -10,7 +10,10 @@
 #include <linux/lockdep.h>
 
 #include "i915_active_types.h"
-#include "i915_request.h"
+
+struct i915_request;
+struct intel_engine_cs;
+struct intel_timeline;
 
 /*
  * We treat requests as fences. This is not be to confused with our
@@ -28,308 +31,108 @@
  * write access so that we can perform concurrent read operations between
  * the CPU and GPU engines, as well as waiting for all rendering to
  * complete, or waiting for the last GPU user of a "fence register". The
- * object then embeds a #i915_active_request to track the most recent (in
+ * object then embeds a #i915_active_fence to track the most recent (in
  * retirement order) request relevant for the desired mode of access.
- * The #i915_active_request is updated with i915_active_request_set() to
+ * The #i915_active_fence is updated with i915_active_fence_set() to
  * track the most recent fence request, typically this is done as part of
  * i915_vma_move_to_active().
  *
- * When the #i915_active_request completes (is retired), it will
+ * When the #i915_active_fence completes (is retired), it will
  * signal its completion to the owner through a callback as well as mark
- * itself as idle (i915_active_request.request == NULL). The owner
+ * itself as idle (i915_active_fence.request == NULL). The owner
  * can then perform any action, such as delayed freeing of an active
  * resource including itself.
  */
 
-void i915_active_retire_noop(struct i915_active_request *active,
-			     struct i915_request *request);
+void i915_active_noop(struct dma_fence *fence, struct dma_fence_cb *cb);
 
 /**
- * i915_active_request_init - prepares the activity tracker for use
+ * __i915_active_fence_init - prepares the activity tracker for use
  * @active - the active tracker
- * @rq - initial request to track, can be NULL
+ * @fence - initial fence to track, can be NULL
  * @func - a callback when then the tracker is retired (becomes idle),
  *         can be NULL
  *
- * i915_active_request_init() prepares the embedded @active struct for use as
- * an activity tracker, that is for tracking the last known active request
- * associated with it. When the last request becomes idle, when it is retired
+ * i915_active_fence_init() prepares the embedded @active struct for use as
+ * an activity tracker, that is for tracking the last known active fence
+ * associated with it. When the last fence becomes idle, when it is retired
  * after completion, the optional callback @func is invoked.
  */
 static inline void
-i915_active_request_init(struct i915_active_request *active,
+__i915_active_fence_init(struct i915_active_fence *active,
 			 struct mutex *lock,
-			 struct i915_request *rq,
-			 i915_active_retire_fn retire)
+			 void *fence,
+			 dma_fence_func_t fn)
 {
-	RCU_INIT_POINTER(active->request, rq);
-	INIT_LIST_HEAD(&active->link);
-	active->retire = retire ?: i915_active_retire_noop;
+	RCU_INIT_POINTER(active->fence, fence);
+	active->cb.func = fn ?: i915_active_noop;
 #if IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM)
 	active->lock = lock;
 #endif
 }
 
-#define INIT_ACTIVE_REQUEST(name, lock) \
-	i915_active_request_init((name), (lock), NULL, NULL)
-
-/**
- * i915_active_request_set - updates the tracker to watch the current request
- * @active - the active tracker
- * @request - the request to watch
- *
- * __i915_active_request_set() watches the given @request for completion. Whilst
- * that @request is busy, the @active reports busy. When that @request is
- * retired, the @active tracker is updated to report idle.
- */
-static inline void
-__i915_active_request_set(struct i915_active_request *active,
-			  struct i915_request *request)
-{
-#if IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM)
-	lockdep_assert_held(active->lock);
-#endif
-	list_move(&active->link, &request->active_list);
-	rcu_assign_pointer(active->request, request);
-}
+#define INIT_ACTIVE_FENCE(A, LOCK) \
+	__i915_active_fence_init((A), (LOCK), NULL, NULL)
 
-int __must_check
-i915_active_request_set(struct i915_active_request *active,
-			struct i915_request *rq);
+struct dma_fence *
+__i915_active_fence_set(struct i915_active_fence *active,
+			struct dma_fence *fence);
 
 /**
- * i915_active_request_raw - return the active request
+ * i915_active_fence_set - updates the tracker to watch the current fence
  * @active - the active tracker
+ * @rq - the request to watch
  *
- * i915_active_request_raw() returns the current request being tracked, or NULL.
- * It does not obtain a reference on the request for the caller, so the caller
- * must hold struct_mutex.
+ * i915_active_fence_set() watches the given @rq for completion. While
+ * that @rq is busy, the @active reports busy. When that @rq is signaled
+ * (or else retired) the @active tracker is updated to report idle.
  */
-static inline struct i915_request *
-i915_active_request_raw(const struct i915_active_request *active,
-			struct mutex *mutex)
-{
-	return rcu_dereference_protected(active->request,
-					 lockdep_is_held(mutex));
-}
-
-/**
- * i915_active_request_peek - report the active request being monitored
- * @active - the active tracker
- *
- * i915_active_request_peek() returns the current request being tracked if
- * still active, or NULL. It does not obtain a reference on the request
- * for the caller, so the caller must hold struct_mutex.
- */
-static inline struct i915_request *
-i915_active_request_peek(const struct i915_active_request *active,
-			 struct mutex *mutex)
-{
-	struct i915_request *request;
-
-	request = i915_active_request_raw(active, mutex);
-	if (!request || i915_request_completed(request))
-		return NULL;
-
-	return request;
-}
-
-/**
- * i915_active_request_get - return a reference to the active request
- * @active - the active tracker
- *
- * i915_active_request_get() returns a reference to the active request, or NULL
- * if the active tracker is idle. The caller must hold struct_mutex.
- */
-static inline struct i915_request *
-i915_active_request_get(const struct i915_active_request *active,
-			struct mutex *mutex)
-{
-	return i915_request_get(i915_active_request_peek(active, mutex));
-}
-
-/**
- * __i915_active_request_get_rcu - return a reference to the active request
- * @active - the active tracker
- *
- * __i915_active_request_get() returns a reference to the active request,
- * or NULL if the active tracker is idle. The caller must hold the RCU read
- * lock, but the returned pointer is safe to use outside of RCU.
- */
-static inline struct i915_request *
-__i915_active_request_get_rcu(const struct i915_active_request *active)
-{
-	/*
-	 * Performing a lockless retrieval of the active request is super
-	 * tricky. SLAB_TYPESAFE_BY_RCU merely guarantees that the backing
-	 * slab of request objects will not be freed whilst we hold the
-	 * RCU read lock. It does not guarantee that the request itself
-	 * will not be freed and then *reused*. Viz,
-	 *
-	 * Thread A			Thread B
-	 *
-	 * rq = active.request
-	 *				retire(rq) -> free(rq);
-	 *				(rq is now first on the slab freelist)
-	 *				active.request = NULL
-	 *
-	 *				rq = new submission on a new object
-	 * ref(rq)
-	 *
-	 * To prevent the request from being reused whilst the caller
-	 * uses it, we take a reference like normal. Whilst acquiring
-	 * the reference we check that it is not in a destroyed state
-	 * (refcnt == 0). That prevents the request being reallocated
-	 * whilst the caller holds on to it. To check that the request
-	 * was not reallocated as we acquired the reference we have to
-	 * check that our request remains the active request across
-	 * the lookup, in the same manner as a seqlock. The visibility
-	 * of the pointer versus the reference counting is controlled
-	 * by using RCU barriers (rcu_dereference and rcu_assign_pointer).
-	 *
-	 * In the middle of all that, we inspect whether the request is
-	 * complete. Retiring is lazy so the request may be completed long
-	 * before the active tracker is updated. Querying whether the
-	 * request is complete is far cheaper (as it involves no locked
-	 * instructions setting cachelines to exclusive) than acquiring
-	 * the reference, so we do it first. The RCU read lock ensures the
-	 * pointer dereference is valid, but does not ensure that the
-	 * seqno nor HWS is the right one! However, if the request was
-	 * reallocated, that means the active tracker's request was complete.
-	 * If the new request is also complete, then both are and we can
-	 * just report the active tracker is idle. If the new request is
-	 * incomplete, then we acquire a reference on it and check that
-	 * it remained the active request.
-	 *
-	 * It is then imperative that we do not zero the request on
-	 * reallocation, so that we can chase the dangling pointers!
-	 * See i915_request_alloc().
-	 */
-	do {
-		struct i915_request *request;
-
-		request = rcu_dereference(active->request);
-		if (!request || i915_request_completed(request))
-			return NULL;
-
-		/*
-		 * An especially silly compiler could decide to recompute the
-		 * result of i915_request_completed, more specifically
-		 * re-emit the load for request->fence.seqno. A race would catch
-		 * a later seqno value, which could flip the result from true to
-		 * false. Which means part of the instructions below might not
-		 * be executed, while later on instructions are executed. Due to
-		 * barriers within the refcounting the inconsistency can't reach
-		 * past the call to i915_request_get_rcu, but not executing
-		 * that while still executing i915_request_put() creates
-		 * havoc enough.  Prevent this with a compiler barrier.
-		 */
-		barrier();
-
-		request = i915_request_get_rcu(request);
-
-		/*
-		 * What stops the following rcu_access_pointer() from occurring
-		 * before the above i915_request_get_rcu()? If we were
-		 * to read the value before pausing to get the reference to
-		 * the request, we may not notice a change in the active
-		 * tracker.
-		 *
-		 * The rcu_access_pointer() is a mere compiler barrier, which
-		 * means both the CPU and compiler are free to perform the
-		 * memory read without constraint. The compiler only has to
-		 * ensure that any operations after the rcu_access_pointer()
-		 * occur afterwards in program order. This means the read may
-		 * be performed earlier by an out-of-order CPU, or adventurous
-		 * compiler.
-		 *
-		 * The atomic operation at the heart of
-		 * i915_request_get_rcu(), see dma_fence_get_rcu(), is
-		 * atomic_inc_not_zero() which is only a full memory barrier
-		 * when successful. That is, if i915_request_get_rcu()
-		 * returns the request (and so with the reference counted
-		 * incremented) then the following read for rcu_access_pointer()
-		 * must occur after the atomic operation and so confirm
-		 * that this request is the one currently being tracked.
-		 *
-		 * The corresponding write barrier is part of
-		 * rcu_assign_pointer().
-		 */
-		if (!request || request == rcu_access_pointer(active->request))
-			return rcu_pointer_handoff(request);
-
-		i915_request_put(request);
-	} while (1);
-}
-
+int __must_check
+i915_active_fence_set(struct i915_active_fence *active,
+		      struct i915_request *rq);
 /**
- * i915_active_request_get_unlocked - return a reference to the active request
+ * i915_active_fence_get - return a reference to the active fence
  * @active - the active tracker
  *
- * i915_active_request_get_unlocked() returns a reference to the active request,
+ * i915_active_fence_get() returns a reference to the active fence,
  * or NULL if the active tracker is idle. The reference is obtained under RCU,
  * so no locking is required by the caller.
  *
- * The reference should be freed with i915_request_put().
+ * The reference should be freed with dma_fence_put().
  */
-static inline struct i915_request *
-i915_active_request_get_unlocked(const struct i915_active_request *active)
+static inline struct dma_fence *
+i915_active_fence_get(struct i915_active_fence *active)
 {
-	struct i915_request *request;
+	struct dma_fence *fence;
 
 	rcu_read_lock();
-	request = __i915_active_request_get_rcu(active);
+	fence = dma_fence_get_rcu_safe(&active->fence);
 	rcu_read_unlock();
 
-	return request;
+	return fence;
 }
 
 /**
- * i915_active_request_isset - report whether the active tracker is assigned
+ * i915_active_fence_isset - report whether the active tracker is assigned
  * @active - the active tracker
  *
- * i915_active_request_isset() returns true if the active tracker is currently
- * assigned to a request. Due to the lazy retiring, that request may be idle
+ * i915_active_fence_isset() returns true if the active tracker is currently
+ * assigned to a fence. Due to the lazy retiring, that fence may be idle
  * and this may report stale information.
  */
 static inline bool
-i915_active_request_isset(const struct i915_active_request *active)
+i915_active_fence_isset(const struct i915_active_fence *active)
 {
-	return rcu_access_pointer(active->request);
+	return rcu_access_pointer(active->fence);
 }
 
-/**
- * i915_active_request_retire - waits until the request is retired
- * @active - the active request on which to wait
- *
- * i915_active_request_retire() waits until the request is completed,
- * and then ensures that at least the retirement handler for this
- * @active tracker is called before returning. If the @active
- * tracker is idle, the function returns immediately.
- */
-static inline int __must_check
-i915_active_request_retire(struct i915_active_request *active,
-			   struct mutex *mutex)
+static inline void
+i915_active_fence_cb(struct dma_fence *fence, struct dma_fence_cb *cb)
 {
-	struct i915_request *request;
-	long ret;
-
-	request = i915_active_request_raw(active, mutex);
-	if (!request)
-		return 0;
-
-	ret = i915_request_wait(request,
-				I915_WAIT_INTERRUPTIBLE,
-				MAX_SCHEDULE_TIMEOUT);
-	if (ret < 0)
-		return ret;
+	struct i915_active_fence *active =
+		container_of(cb, typeof(*active), cb);
 
-	list_del_init(&active->link);
-	RCU_INIT_POINTER(active->request, NULL);
-
-	active->retire(active, request);
-
-	return 0;
+	RCU_INIT_POINTER(active->fence, NULL);
 }
 
 /*
@@ -358,41 +161,34 @@ i915_active_request_retire(struct i915_active_request *active,
  * synchronisation.
  */
 
-void __i915_active_init(struct drm_i915_private *i915,
-			struct i915_active *ref,
+void __i915_active_init(struct i915_active *ref,
 			int (*active)(struct i915_active *ref),
 			void (*retire)(struct i915_active *ref),
 			struct lock_class_key *key);
-#define i915_active_init(i915, ref, active, retire) do {		\
+#define i915_active_init(ref, active, retire) do {		\
 	static struct lock_class_key __key;				\
 									\
-	__i915_active_init(i915, ref, active, retire, &__key);		\
+	__i915_active_init(ref, active, retire, &__key);		\
 } while (0)
 
 int i915_active_ref(struct i915_active *ref,
 		    struct intel_timeline *tl,
-		    struct i915_request *rq);
+		    struct dma_fence *fence);
 
 void i915_active_set_exclusive(struct i915_active *ref, struct dma_fence *f);
 
 static inline bool i915_active_has_exclusive(struct i915_active *ref)
 {
-	return rcu_access_pointer(ref->excl);
+	return rcu_access_pointer(ref->excl.fence);
 }
 
 int i915_active_wait(struct i915_active *ref);
 
-int i915_request_await_active(struct i915_request *rq,
-			      struct i915_active *ref);
-int i915_request_await_active_request(struct i915_request *rq,
-				      struct i915_active_request *active);
+int i915_request_await_active(struct i915_request *rq, struct i915_active *ref);
 
 int i915_active_acquire(struct i915_active *ref);
+bool i915_active_acquire_if_busy(struct i915_active *ref);
 void i915_active_release(struct i915_active *ref);
-void __i915_active_release_nested(struct i915_active *ref, int subclass);
-
-bool i915_active_trygrab(struct i915_active *ref);
-void i915_active_ungrab(struct i915_active *ref);
 
 static inline bool
 i915_active_is_idle(const struct i915_active *ref)
diff --git a/drivers/gpu/drm/i915/i915_active_types.h b/drivers/gpu/drm/i915/i915_active_types.h
index 021167f0004d..d89a74c142c6 100644
--- a/drivers/gpu/drm/i915/i915_active_types.h
+++ b/drivers/gpu/drm/i915/i915_active_types.h
@@ -17,17 +17,9 @@
 
 #include "i915_utils.h"
 
-struct drm_i915_private;
-struct i915_active_request;
-struct i915_request;
-
-typedef void (*i915_active_retire_fn)(struct i915_active_request *,
-				      struct i915_request *);
-
-struct i915_active_request {
-	struct i915_request __rcu *request;
-	struct list_head link;
-	i915_active_retire_fn retire;
+struct i915_active_fence {
+	struct dma_fence __rcu *fence;
+	struct dma_fence_cb cb;
 #if IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM)
 	/*
 	 * Incorporeal!
@@ -53,20 +45,17 @@ struct active_node;
 #define i915_active_may_sleep(fn) ptr_pack_bits(&(fn), I915_ACTIVE_MAY_SLEEP, 2)
 
 struct i915_active {
-	struct drm_i915_private *i915;
+	atomic_t count;
+	struct mutex mutex;
 
 	struct active_node *cache;
 	struct rb_root tree;
-	struct mutex mutex;
-	atomic_t count;
 
 	/* Preallocated "exclusive" node */
-	struct dma_fence __rcu *excl;
-	struct dma_fence_cb excl_cb;
+	struct i915_active_fence excl;
 
 	unsigned long flags;
 #define I915_ACTIVE_RETIRE_SLEEPS BIT(0)
-#define I915_ACTIVE_GRAB_BIT 1
 
 	int (*active)(struct i915_active *ref);
 	void (*retire)(struct i915_active *ref);
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 3eed2efa8d13..1aadab1cdd24 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -900,28 +900,38 @@ wait_for_timelines(struct drm_i915_private *i915,
 
 	spin_lock_irqsave(&timelines->lock, flags);
 	list_for_each_entry(tl, &timelines->active_list, link) {
-		struct i915_request *rq;
+		struct dma_fence *fence;
 
-		rq = i915_active_request_get_unlocked(&tl->last_request);
-		if (!rq)
+		fence = i915_active_fence_get(&tl->last_request);
+		if (!fence)
 			continue;
 
 		spin_unlock_irqrestore(&timelines->lock, flags);
 
-		/*
-		 * "Race-to-idle".
-		 *
-		 * Switching to the kernel context is often used a synchronous
-		 * step prior to idling, e.g. in suspend for flushing all
-		 * current operations to memory before sleeping. These we
-		 * want to complete as quickly as possible to avoid prolonged
-		 * stalls, so allow the gpu to boost to maximum clocks.
-		 */
-		if (wait & I915_WAIT_FOR_IDLE_BOOST)
-			gen6_rps_boost(rq);
+		if (!dma_fence_is_i915(fence)) {
+			timeout = dma_fence_wait_timeout(fence,
+							 flags & I915_WAIT_INTERRUPTIBLE,
+							 timeout);
+		} else {
+			struct i915_request *rq = to_request(fence);
+
+			/*
+			 * "Race-to-idle".
+			 *
+			 * Switching to the kernel context is often used as
+			 * a synchronous step prior to idling, e.g. in suspend
+			 * for flushing all current operations to memory before
+			 * sleeping. These we want to complete as quickly as
+			 * possible to avoid prolonged stalls, so allow the gpu
+			 * to boost to maximum clocks.
+			 */
+			if (flags & I915_WAIT_FOR_IDLE_BOOST)
+				gen6_rps_boost(rq);
+
+			timeout = i915_request_wait(rq, flags, timeout);
+		}
 
-		timeout = i915_request_wait(rq, wait, timeout);
-		i915_request_put(rq);
+		dma_fence_put(fence);
 		if (timeout < 0)
 			return timeout;
 
diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
index b2e4827788fc..60676de059a7 100644
--- a/drivers/gpu/drm/i915/i915_gem_gtt.c
+++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
@@ -1858,7 +1858,6 @@ static const struct i915_vma_ops pd_vma_ops = {
 
 static struct i915_vma *pd_vma_create(struct gen6_ppgtt *ppgtt, int size)
 {
-	struct drm_i915_private *i915 = ppgtt->base.vm.i915;
 	struct i915_ggtt *ggtt = ppgtt->base.vm.gt->ggtt;
 	struct i915_vma *vma;
 
@@ -1869,7 +1868,7 @@ static struct i915_vma *pd_vma_create(struct gen6_ppgtt *ppgtt, int size)
 	if (!vma)
 		return ERR_PTR(-ENOMEM);
 
-	i915_active_init(i915, &vma->active, NULL, NULL);
+	i915_active_init(&vma->active, NULL, NULL);
 
 	mutex_init(&vma->pages_mutex);
 	vma->vm = i915_vm_get(&ggtt->vm);
diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c
index 3ccf7fd9307f..2c0071ff2d2d 100644
--- a/drivers/gpu/drm/i915/i915_gpu_error.c
+++ b/drivers/gpu/drm/i915/i915_gpu_error.c
@@ -1292,7 +1292,7 @@ capture_vma(struct capture_vma *next,
 	if (!c)
 		return next;
 
-	if (!i915_active_trygrab(&vma->active)) {
+	if (!i915_active_acquire_if_busy(&vma->active)) {
 		kfree(c);
 		return next;
 	}
@@ -1432,7 +1432,7 @@ gem_record_rings(struct i915_gpu_state *error, struct compress *compress)
 			*this->slot =
 				i915_error_object_create(i915, vma, compress);
 
-			i915_active_ungrab(&vma->active);
+			i915_active_release(&vma->active);
 			i915_vma_put(vma);
 
 			capture = this->next;
diff --git a/drivers/gpu/drm/i915/i915_request.c b/drivers/gpu/drm/i915/i915_request.c
index 754a78364a63..4ecfae143276 100644
--- a/drivers/gpu/drm/i915/i915_request.c
+++ b/drivers/gpu/drm/i915/i915_request.c
@@ -197,9 +197,8 @@ static void free_capture_list(struct i915_request *request)
 
 static bool i915_request_retire(struct i915_request *rq)
 {
-	struct i915_active_request *active, *next;
-
 	lockdep_assert_held(&rq->timeline->mutex);
+
 	if (!i915_request_completed(rq))
 		return false;
 
@@ -223,35 +222,6 @@ static bool i915_request_retire(struct i915_request *rq)
 	GEM_BUG_ON(!list_is_first(&rq->link, &rq->timeline->requests));
 	rq->ring->head = rq->postfix;
 
-	/*
-	 * Walk through the active list, calling retire on each. This allows
-	 * objects to track their GPU activity and mark themselves as idle
-	 * when their *last* active request is completed (updating state
-	 * tracking lists for eviction, active references for GEM, etc).
-	 *
-	 * As the ->retire() may free the node, we decouple it first and
-	 * pass along the auxiliary information (to avoid dereferencing
-	 * the node after the callback).
-	 */
-	list_for_each_entry_safe(active, next, &rq->active_list, link) {
-		/*
-		 * In microbenchmarks or focusing upon time inside the kernel,
-		 * we may spend an inordinate amount of time simply handling
-		 * the retirement of requests and processing their callbacks.
-		 * Of which, this loop itself is particularly hot due to the
-		 * cache misses when jumping around the list of
-		 * i915_active_request.  So we try to keep this loop as
-		 * streamlined as possible and also prefetch the next
-		 * i915_active_request to try and hide the likely cache miss.
-		 */
-		prefetchw(next);
-
-		INIT_LIST_HEAD(&active->link);
-		RCU_INIT_POINTER(active->request, NULL);
-
-		active->retire(active, rq);
-	}
-
 	local_irq_disable();
 
 	/*
@@ -664,7 +634,6 @@ __i915_request_create(struct intel_context *ce, gfp_t gfp)
 	rq->flags = 0;
 	rq->execution_mask = ALL_ENGINES;
 
-	INIT_LIST_HEAD(&rq->active_list);
 	INIT_LIST_HEAD(&rq->execute_cb);
 
 	/*
@@ -703,7 +672,6 @@ __i915_request_create(struct intel_context *ce, gfp_t gfp)
 	ce->ring->emit = rq->head;
 
 	/* Make sure we didn't add ourselves to external state before freeing */
-	GEM_BUG_ON(!list_empty(&rq->active_list));
 	GEM_BUG_ON(!list_empty(&rq->sched.signalers_list));
 	GEM_BUG_ON(!list_empty(&rq->sched.waiters_list));
 
@@ -1096,8 +1064,8 @@ __i915_request_add_to_timeline(struct i915_request *rq)
 	 * precludes optimising to use semaphores serialisation of a single
 	 * timeline across engines.
 	 */
-	prev = rcu_dereference_protected(timeline->last_request.request,
-					 lockdep_is_held(&timeline->mutex));
+	prev = to_request(__i915_active_fence_set(&timeline->last_request,
+						  &rq->fence));
 	if (prev && !i915_request_completed(prev)) {
 		if (is_power_of_2(prev->engine->mask | rq->engine->mask))
 			i915_sw_fence_await_sw_fence(&rq->submit,
@@ -1122,7 +1090,6 @@ __i915_request_add_to_timeline(struct i915_request *rq)
 	 * us, the timeline will hold its seqno which is later than ours.
 	 */
 	GEM_BUG_ON(timeline->seqno != rq->fence.seqno);
-	__i915_active_request_set(&timeline->last_request, rq);
 
 	return prev;
 }
diff --git a/drivers/gpu/drm/i915/i915_request.h b/drivers/gpu/drm/i915/i915_request.h
index 8ac6e1226a56..3251d2bdbeea 100644
--- a/drivers/gpu/drm/i915/i915_request.h
+++ b/drivers/gpu/drm/i915/i915_request.h
@@ -211,7 +211,6 @@ struct i915_request {
 	 * on the active_list (of their final request).
 	 */
 	struct i915_capture_list *capture_list;
-	struct list_head active_list;
 
 	/** Time at which this request was emitted, in jiffies. */
 	unsigned long emitted_jiffies;
diff --git a/drivers/gpu/drm/i915/i915_vma.c b/drivers/gpu/drm/i915/i915_vma.c
index 5ec8c970f9a2..2c703fd3c2b9 100644
--- a/drivers/gpu/drm/i915/i915_vma.c
+++ b/drivers/gpu/drm/i915/i915_vma.c
@@ -120,8 +120,7 @@ vma_create(struct drm_i915_gem_object *obj,
 	vma->size = obj->base.size;
 	vma->display_alignment = I915_GTT_MIN_ALIGNMENT;
 
-	i915_active_init(vm->i915, &vma->active,
-			 __i915_vma_active, __i915_vma_retire);
+	i915_active_init(&vma->active, __i915_vma_active, __i915_vma_retire);
 
 	/* Declare ourselves safe for use inside shrinkers */
 	if (IS_ENABLED(CONFIG_LOCKDEP)) {
@@ -1100,7 +1099,7 @@ int i915_vma_move_to_active(struct i915_vma *vma,
 	 * add the active reference first and queue for it to be dropped
 	 * *last*.
 	 */
-	err = i915_active_ref(&vma->active, rq->timeline, rq);
+	err = i915_active_ref(&vma->active, rq->timeline, &rq->fence);
 	if (unlikely(err))
 		return err;
 
@@ -1108,7 +1107,7 @@ int i915_vma_move_to_active(struct i915_vma *vma,
 		if (intel_frontbuffer_invalidate(obj->frontbuffer, ORIGIN_CS))
 			i915_active_ref(&obj->frontbuffer->write,
 					rq->timeline,
-					rq);
+					&rq->fence);
 
 		dma_resv_add_excl_fence(vma->resv, &rq->fence);
 		obj->write_domain = I915_GEM_DOMAIN_RENDER;
@@ -1146,6 +1145,7 @@ int i915_vma_unbind(struct i915_vma *vma)
 	if (ret)
 		return ret;
 
+	GEM_BUG_ON(i915_vma_is_active(vma));
 	if (i915_vma_is_pinned(vma)) {
 		vma_print_allocator(vma, "is pinned");
 		return -EBUSY;
diff --git a/drivers/gpu/drm/i915/selftests/i915_active.c b/drivers/gpu/drm/i915/selftests/i915_active.c
index d5ac9944d093..af5827aac7b2 100644
--- a/drivers/gpu/drm/i915/selftests/i915_active.c
+++ b/drivers/gpu/drm/i915/selftests/i915_active.c
@@ -68,7 +68,7 @@ static struct live_active *__live_alloc(struct drm_i915_private *i915)
 		return NULL;
 
 	kref_init(&active->ref);
-	i915_active_init(i915, &active->base, __live_active, __live_retire);
+	i915_active_init(&active->base, __live_active, __live_retire);
 
 	return active;
 }
@@ -110,7 +110,9 @@ __live_active_setup(struct drm_i915_private *i915)
 						       submit,
 						       GFP_KERNEL);
 		if (err >= 0)
-			err = i915_active_ref(&active->base, rq->timeline, rq);
+			err = i915_active_ref(&active->base,
+					      rq->timeline,
+					      &rq->fence);
 		i915_request_add(rq);
 		if (err) {
 			pr_err("Failed to track active ref!\n");
@@ -146,19 +148,13 @@ static int live_active_wait(void *arg)
 {
 	struct drm_i915_private *i915 = arg;
 	struct live_active *active;
-	intel_wakeref_t wakeref;
 	int err = 0;
 
 	/* Check that we get a callback when requests retire upon waiting */
 
-	mutex_lock(&i915->drm.struct_mutex);
-	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
-
 	active = __live_active_setup(i915);
-	if (IS_ERR(active)) {
-		err = PTR_ERR(active);
-		goto err;
-	}
+	if (IS_ERR(active))
+		return PTR_ERR(active);
 
 	i915_active_wait(&active->base);
 	if (!READ_ONCE(active->retired)) {
@@ -168,11 +164,9 @@ static int live_active_wait(void *arg)
 
 	__live_put(active);
 
+	mutex_lock(&i915->drm.struct_mutex);
 	if (igt_flush_test(i915, I915_WAIT_LOCKED))
 		err = -EIO;
-
-err:
-	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
 	mutex_unlock(&i915->drm.struct_mutex);
 
 	return err;
@@ -182,23 +176,19 @@ static int live_active_retire(void *arg)
 {
 	struct drm_i915_private *i915 = arg;
 	struct live_active *active;
-	intel_wakeref_t wakeref;
 	int err = 0;
 
 	/* Check that we get a callback when requests are indirectly retired */
 
-	mutex_lock(&i915->drm.struct_mutex);
-	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
-
 	active = __live_active_setup(i915);
-	if (IS_ERR(active)) {
-		err = PTR_ERR(active);
-		goto err;
-	}
+	if (IS_ERR(active))
+		return PTR_ERR(active);
 
 	/* waits for & retires all requests */
+	mutex_lock(&i915->drm.struct_mutex);
 	if (igt_flush_test(i915, I915_WAIT_LOCKED))
 		err = -EIO;
+	mutex_unlock(&i915->drm.struct_mutex);
 
 	if (!READ_ONCE(active->retired)) {
 		pr_err("i915_active not retired after flushing!\n");
@@ -207,10 +197,6 @@ static int live_active_retire(void *arg)
 
 	__live_put(active);
 
-err:
-	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
-	mutex_unlock(&i915->drm.struct_mutex);
-
 	return err;
 }
 
-- 
2.23.0

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

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

* [PATCH 16/21] drm/i915: Move idle barrier cleanup into engine-pm
  2019-09-02  4:02 [PATCH 01/21] drm/i915: Restrict the aliasing-ppgtt to the size of the ggtt Chris Wilson
                   ` (13 preceding siblings ...)
  2019-09-02  4:02 ` [PATCH 15/21] drm/i915: Coordinate i915_active with its own mutex Chris Wilson
@ 2019-09-02  4:02 ` Chris Wilson
  2019-09-20 16:18   ` Tvrtko Ursulin
  2019-09-02  4:02 ` [PATCH 17/21] drm/i915: Drop struct_mutex from around i915_retire_requests() Chris Wilson
                   ` (8 subsequent siblings)
  23 siblings, 1 reply; 53+ messages in thread
From: Chris Wilson @ 2019-09-02  4:02 UTC (permalink / raw)
  To: intel-gfx

Now that we now longer need to guarantee that the active callback is
under the struct_mutex, we can lift it out of the i915_gem_park() and
into the engine parking itself.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 drivers/gpu/drm/i915/gem/i915_gem_pm.c    | 19 -------------------
 drivers/gpu/drm/i915/gt/intel_engine_pm.c | 15 +++++++++++++++
 drivers/gpu/drm/i915/i915_active.c        |  1 +
 3 files changed, 16 insertions(+), 19 deletions(-)

diff --git a/drivers/gpu/drm/i915/gem/i915_gem_pm.c b/drivers/gpu/drm/i915/gem/i915_gem_pm.c
index 92558fa47108..6e4cc177cc7a 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_pm.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_pm.c
@@ -11,29 +11,10 @@
 #include "i915_drv.h"
 #include "i915_globals.h"
 
-static void call_idle_barriers(struct intel_engine_cs *engine)
-{
-	struct llist_node *node, *next;
-
-	llist_for_each_safe(node, next, llist_del_all(&engine->barrier_tasks)) {
-		struct dma_fence_cb *cb =
-			container_of((struct list_head *)node,
-				     typeof(*cb), node);
-
-		cb->func(NULL, cb);
-	}
-}
-
 static void i915_gem_park(struct drm_i915_private *i915)
 {
-	struct intel_engine_cs *engine;
-	enum intel_engine_id id;
-
 	lockdep_assert_held(&i915->drm.struct_mutex);
 
-	for_each_engine(engine, i915, id)
-		call_idle_barriers(engine); /* cleanup after wedging */
-
 	i915_vma_parked(i915);
 
 	i915_globals_park();
diff --git a/drivers/gpu/drm/i915/gt/intel_engine_pm.c b/drivers/gpu/drm/i915/gt/intel_engine_pm.c
index 65b5ca74b394..472b2259f629 100644
--- a/drivers/gpu/drm/i915/gt/intel_engine_pm.c
+++ b/drivers/gpu/drm/i915/gt/intel_engine_pm.c
@@ -123,6 +123,19 @@ static bool switch_to_kernel_context(struct intel_engine_cs *engine)
 	return result;
 }
 
+static void call_idle_barriers(struct intel_engine_cs *engine)
+{
+	struct llist_node *node, *next;
+
+	llist_for_each_safe(node, next, llist_del_all(&engine->barrier_tasks)) {
+		struct dma_fence_cb *cb =
+			container_of((struct list_head *)node,
+				     typeof(*cb), node);
+
+		cb->func(NULL, cb);
+	}
+}
+
 static int __engine_park(struct intel_wakeref *wf)
 {
 	struct intel_engine_cs *engine =
@@ -142,6 +155,8 @@ static int __engine_park(struct intel_wakeref *wf)
 
 	GEM_TRACE("%s\n", engine->name);
 
+	call_idle_barriers(engine); /* cleanup after wedging */
+
 	intel_engine_disarm_breadcrumbs(engine);
 	intel_engine_pool_park(&engine->pool);
 
diff --git a/drivers/gpu/drm/i915/i915_active.c b/drivers/gpu/drm/i915/i915_active.c
index 2e2ab8176620..dcf5bc1d87e6 100644
--- a/drivers/gpu/drm/i915/i915_active.c
+++ b/drivers/gpu/drm/i915/i915_active.c
@@ -679,6 +679,7 @@ void i915_active_acquire_barrier(struct i915_active *ref)
 		rb_link_node(&node->node, parent, p);
 		rb_insert_color(&node->node, &ref->tree);
 
+		GEM_BUG_ON(!intel_engine_pm_is_awake(engine));
 		llist_add(barrier_to_ll(node), &engine->barrier_tasks);
 		intel_engine_pm_put(engine);
 	}
-- 
2.23.0

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

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

* [PATCH 17/21] drm/i915: Drop struct_mutex from around i915_retire_requests()
  2019-09-02  4:02 [PATCH 01/21] drm/i915: Restrict the aliasing-ppgtt to the size of the ggtt Chris Wilson
                   ` (14 preceding siblings ...)
  2019-09-02  4:02 ` [PATCH 16/21] drm/i915: Move idle barrier cleanup into engine-pm Chris Wilson
@ 2019-09-02  4:02 ` Chris Wilson
  2019-09-24 15:25   ` Tvrtko Ursulin
  2019-09-02  4:03 ` [PATCH 18/21] drm/i915: Remove the GEM idle worker Chris Wilson
                   ` (7 subsequent siblings)
  23 siblings, 1 reply; 53+ messages in thread
From: Chris Wilson @ 2019-09-02  4:02 UTC (permalink / raw)
  To: intel-gfx

We don't need to hold struct_mutex now for retiring requests, so drop it
from i915_retire_requests() and i915_gem_wait_for_idle(), finally
removing I915_WAIT_LOCKED for good.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 .../gpu/drm/i915/gem/i915_gem_client_blt.c    |   7 +-
 drivers/gpu/drm/i915/gem/i915_gem_context.c   |  20 +--
 drivers/gpu/drm/i915/gem/i915_gem_pm.c        |  45 +++----
 .../i915/gem/selftests/i915_gem_coherency.c   |  40 +++---
 .../drm/i915/gem/selftests/i915_gem_context.c |   4 +-
 .../drm/i915/gem/selftests/i915_gem_mman.c    |   6 +-
 .../i915/gem/selftests/i915_gem_object_blt.c  |   4 -
 drivers/gpu/drm/i915/gt/selftest_context.c    |   4 +-
 drivers/gpu/drm/i915/gt/selftest_hangcheck.c  |  89 +++----------
 drivers/gpu/drm/i915/gt/selftest_lrc.c        |  21 ++-
 drivers/gpu/drm/i915/gt/selftest_timeline.c   |  91 ++++++-------
 .../gpu/drm/i915/gt/selftest_workarounds.c    |   6 +-
 drivers/gpu/drm/i915/i915_debugfs.c           |  42 ++----
 drivers/gpu/drm/i915/i915_gem.c               |  19 ++-
 drivers/gpu/drm/i915/i915_request.h           |   7 +-
 drivers/gpu/drm/i915/selftests/i915_active.c  |   8 +-
 .../gpu/drm/i915/selftests/i915_gem_evict.c   |   2 +-
 drivers/gpu/drm/i915/selftests/i915_gem_gtt.c |   4 -
 drivers/gpu/drm/i915/selftests/i915_request.c | 125 +++++-------------
 .../gpu/drm/i915/selftests/i915_selftest.c    |   8 +-
 drivers/gpu/drm/i915/selftests/i915_vma.c     |   4 -
 .../gpu/drm/i915/selftests/igt_flush_test.c   |  30 ++---
 .../gpu/drm/i915/selftests/igt_flush_test.h   |   2 +-
 .../gpu/drm/i915/selftests/igt_live_test.c    |   9 +-
 .../gpu/drm/i915/selftests/mock_gem_device.c  |   4 -
 25 files changed, 191 insertions(+), 410 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 ace50bb9ee1f..cf2057e515af 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_client_blt.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_client_blt.c
@@ -166,7 +166,6 @@ static int move_to_active(struct i915_vma *vma, struct i915_request *rq)
 static void clear_pages_worker(struct work_struct *work)
 {
 	struct clear_pages_work *w = container_of(work, typeof(*w), work);
-	struct drm_i915_private *i915 = w->ce->engine->i915;
 	struct drm_i915_gem_object *obj = w->sleeve->vma->obj;
 	struct i915_vma *vma = w->sleeve->vma;
 	struct i915_request *rq;
@@ -184,11 +183,9 @@ static void clear_pages_worker(struct work_struct *work)
 	obj->read_domains = I915_GEM_GPU_DOMAINS;
 	obj->write_domain = 0;
 
-	/* XXX: we need to kill this */
-	mutex_lock(&i915->drm.struct_mutex);
 	err = i915_vma_pin(vma, 0, 0, PIN_USER);
 	if (unlikely(err))
-		goto out_unlock;
+		goto out_signal;
 
 	batch = intel_emit_vma_fill_blt(w->ce, vma, w->value);
 	if (IS_ERR(batch)) {
@@ -240,8 +237,6 @@ static void clear_pages_worker(struct work_struct *work)
 	intel_emit_vma_release(w->ce, batch);
 out_unpin:
 	i915_vma_unpin(vma);
-out_unlock:
-	mutex_unlock(&i915->drm.struct_mutex);
 out_signal:
 	if (unlikely(err)) {
 		dma_fence_set_error(&w->dma, err);
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context.c b/drivers/gpu/drm/i915/gem/i915_gem_context.c
index b8ddcf7899a1..3452f1497094 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_context.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_context.c
@@ -1161,8 +1161,7 @@ gen8_modify_rpcs(struct intel_context *ce, struct intel_sseu sseu)
 }
 
 static int
-__intel_context_reconfigure_sseu(struct intel_context *ce,
-				 struct intel_sseu sseu)
+intel_context_reconfigure_sseu(struct intel_context *ce, struct intel_sseu sseu)
 {
 	int ret;
 
@@ -1185,23 +1184,6 @@ __intel_context_reconfigure_sseu(struct intel_context *ce,
 	return ret;
 }
 
-static int
-intel_context_reconfigure_sseu(struct intel_context *ce, struct intel_sseu sseu)
-{
-	struct drm_i915_private *i915 = ce->engine->i915;
-	int ret;
-
-	ret = mutex_lock_interruptible(&i915->drm.struct_mutex);
-	if (ret)
-		return ret;
-
-	ret = __intel_context_reconfigure_sseu(ce, sseu);
-
-	mutex_unlock(&i915->drm.struct_mutex);
-
-	return ret;
-}
-
 static int
 user_to_context_sseu(struct drm_i915_private *i915,
 		     const struct drm_i915_gem_context_param_sseu *user,
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_pm.c b/drivers/gpu/drm/i915/gem/i915_gem_pm.c
index 6e4cc177cc7a..fec0b410d1d9 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_pm.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_pm.c
@@ -48,11 +48,7 @@ static void retire_work_handler(struct work_struct *work)
 	struct drm_i915_private *i915 =
 		container_of(work, typeof(*i915), gem.retire_work.work);
 
-	/* Come back later if the device is busy... */
-	if (mutex_trylock(&i915->drm.struct_mutex)) {
-		i915_retire_requests(i915);
-		mutex_unlock(&i915->drm.struct_mutex);
-	}
+	i915_retire_requests(i915);
 
 	queue_delayed_work(i915->wq,
 			   &i915->gem.retire_work,
@@ -86,26 +82,23 @@ static bool switch_to_kernel_context_sync(struct intel_gt *gt)
 {
 	bool result = !intel_gt_is_wedged(gt);
 
-	do {
-		if (i915_gem_wait_for_idle(gt->i915,
-					   I915_WAIT_LOCKED |
-					   I915_WAIT_FOR_IDLE_BOOST,
-					   I915_GEM_IDLE_TIMEOUT) == -ETIME) {
-			/* XXX hide warning from gem_eio */
-			if (i915_modparams.reset) {
-				dev_err(gt->i915->drm.dev,
-					"Failed to idle engines, declaring wedged!\n");
-				GEM_TRACE_DUMP();
-			}
-
-			/*
-			 * Forcibly cancel outstanding work and leave
-			 * the gpu quiet.
-			 */
-			intel_gt_set_wedged(gt);
-			result = false;
+	if (i915_gem_wait_for_idle(gt->i915,
+				   I915_WAIT_FOR_IDLE_BOOST,
+				   I915_GEM_IDLE_TIMEOUT) == -ETIME) {
+		/* XXX hide warning from gem_eio */
+		if (i915_modparams.reset) {
+			dev_err(gt->i915->drm.dev,
+				"Failed to idle engines, declaring wedged!\n");
+			GEM_TRACE_DUMP();
 		}
-	} while (i915_retire_requests(gt->i915) && result);
+
+		/*
+		 * Forcibly cancel outstanding work and leave
+		 * the gpu quiet.
+		 */
+		intel_gt_set_wedged(gt);
+		result = false;
+	}
 
 	if (intel_gt_pm_wait_for_idle(gt))
 		result = false;
@@ -125,8 +118,6 @@ void i915_gem_suspend(struct drm_i915_private *i915)
 	intel_wakeref_auto(&i915->ggtt.userfault_wakeref, 0);
 	flush_workqueue(i915->wq);
 
-	mutex_lock(&i915->drm.struct_mutex);
-
 	/*
 	 * We have to flush all the executing contexts to main memory so
 	 * that they can saved in the hibernation image. To ensure the last
@@ -138,8 +129,6 @@ void i915_gem_suspend(struct drm_i915_private *i915)
 	 */
 	switch_to_kernel_context_sync(&i915->gt);
 
-	mutex_unlock(&i915->drm.struct_mutex);
-
 	cancel_delayed_work_sync(&i915->gt.hangcheck.work);
 
 	i915_gem_drain_freed_objects(i915);
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 0ff7a89aadca..549810f70aeb 100644
--- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_coherency.c
+++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_coherency.c
@@ -7,6 +7,7 @@
 #include <linux/prime_numbers.h>
 
 #include "gt/intel_gt.h"
+#include "gt/intel_gt_pm.h"
 
 #include "i915_selftest.h"
 #include "selftests/i915_random.h"
@@ -78,7 +79,7 @@ static int gtt_set(struct drm_i915_gem_object *obj,
 {
 	struct i915_vma *vma;
 	u32 __iomem *map;
-	int err;
+	int err = 0;
 
 	i915_gem_object_lock(obj);
 	err = i915_gem_object_set_to_gtt_domain(obj, true);
@@ -90,15 +91,21 @@ static int gtt_set(struct drm_i915_gem_object *obj,
 	if (IS_ERR(vma))
 		return PTR_ERR(vma);
 
+	intel_gt_pm_get(vma->vm->gt);
+
 	map = i915_vma_pin_iomap(vma);
 	i915_vma_unpin(vma);
-	if (IS_ERR(map))
-		return PTR_ERR(map);
+	if (IS_ERR(map)) {
+		err = PTR_ERR(map);
+		goto out_rpm;
+	}
 
 	iowrite32(v, &map[offset / sizeof(*map)]);
 	i915_vma_unpin_iomap(vma);
 
-	return 0;
+out_rpm:
+	intel_gt_pm_put(vma->vm->gt);
+	return err;
 }
 
 static int gtt_get(struct drm_i915_gem_object *obj,
@@ -107,7 +114,7 @@ static int gtt_get(struct drm_i915_gem_object *obj,
 {
 	struct i915_vma *vma;
 	u32 __iomem *map;
-	int err;
+	int err = 0;
 
 	i915_gem_object_lock(obj);
 	err = i915_gem_object_set_to_gtt_domain(obj, false);
@@ -119,15 +126,21 @@ static int gtt_get(struct drm_i915_gem_object *obj,
 	if (IS_ERR(vma))
 		return PTR_ERR(vma);
 
+	intel_gt_pm_get(vma->vm->gt);
+
 	map = i915_vma_pin_iomap(vma);
 	i915_vma_unpin(vma);
-	if (IS_ERR(map))
-		return PTR_ERR(map);
+	if (IS_ERR(map)) {
+		err = PTR_ERR(map);
+		goto out_rpm;
+	}
 
 	*v = ioread32(&map[offset / sizeof(*map)]);
 	i915_vma_unpin_iomap(vma);
 
-	return 0;
+out_rpm:
+	intel_gt_pm_put(vma->vm->gt);
+	return err;
 }
 
 static int wc_set(struct drm_i915_gem_object *obj,
@@ -280,7 +293,6 @@ static int igt_gem_coherency(void *arg)
 	struct drm_i915_private *i915 = arg;
 	const struct igt_coherency_mode *read, *write, *over;
 	struct drm_i915_gem_object *obj;
-	intel_wakeref_t wakeref;
 	unsigned long count, n;
 	u32 *offsets, *values;
 	int err = 0;
@@ -299,8 +311,6 @@ static int igt_gem_coherency(void *arg)
 
 	values = offsets + ncachelines;
 
-	mutex_lock(&i915->drm.struct_mutex);
-	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
 	for (over = igt_coherency_mode; over->name; over++) {
 		if (!over->set)
 			continue;
@@ -326,7 +336,7 @@ static int igt_gem_coherency(void *arg)
 					obj = i915_gem_object_create_internal(i915, PAGE_SIZE);
 					if (IS_ERR(obj)) {
 						err = PTR_ERR(obj);
-						goto unlock;
+						goto free;
 					}
 
 					i915_random_reorder(offsets, ncachelines, &prng);
@@ -377,15 +387,13 @@ static int igt_gem_coherency(void *arg)
 			}
 		}
 	}
-unlock:
-	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
-	mutex_unlock(&i915->drm.struct_mutex);
+free:
 	kfree(offsets);
 	return err;
 
 put_object:
 	i915_gem_object_put(obj);
-	goto unlock;
+	goto free;
 }
 
 int i915_gem_coherency_live_selftests(struct drm_i915_private *i915)
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 aa67c02ba98c..b87e35a713b8 100644
--- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c
+++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c
@@ -949,7 +949,7 @@ __sseu_test(const char *name,
 	if (ret)
 		return ret;
 
-	ret = __intel_context_reconfigure_sseu(ce, sseu);
+	ret = intel_context_reconfigure_sseu(ce, sseu);
 	if (ret)
 		goto out_spin;
 
@@ -1053,7 +1053,7 @@ __igt_ctx_sseu(struct drm_i915_private *i915,
 		goto out_fail;
 
 out_fail:
-	if (igt_flush_test(i915, I915_WAIT_LOCKED))
+	if (igt_flush_test(i915))
 		ret = -EIO;
 
 	intel_context_unpin(ce);
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 9c217dfe96a9..39c01bc4eb51 100644
--- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c
+++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c
@@ -393,12 +393,8 @@ static void disable_retire_worker(struct drm_i915_private *i915)
 
 static void restore_retire_worker(struct drm_i915_private *i915)
 {
+	igt_flush_test(i915);
 	intel_gt_pm_put(&i915->gt);
-
-	mutex_lock(&i915->drm.struct_mutex);
-	igt_flush_test(i915, I915_WAIT_LOCKED);
-	mutex_unlock(&i915->drm.struct_mutex);
-
 	i915_gem_driver_register__shrinker(i915);
 }
 
diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_object_blt.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_object_blt.c
index c21d747e7d05..9ec55b3a3815 100644
--- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_object_blt.c
+++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_object_blt.c
@@ -65,9 +65,7 @@ static int igt_fill_blt(void *arg)
 		if (!(obj->cache_coherent & I915_BO_CACHE_COHERENT_FOR_WRITE))
 			obj->cache_dirty = true;
 
-		mutex_lock(&i915->drm.struct_mutex);
 		err = i915_gem_object_fill_blt(obj, ce, val);
-		mutex_unlock(&i915->drm.struct_mutex);
 		if (err)
 			goto err_unpin;
 
@@ -166,9 +164,7 @@ static int igt_copy_blt(void *arg)
 		if (!(dst->cache_coherent & I915_BO_CACHE_COHERENT_FOR_WRITE))
 			dst->cache_dirty = true;
 
-		mutex_lock(&i915->drm.struct_mutex);
 		err = i915_gem_object_copy_blt(src, dst, ce);
-		mutex_unlock(&i915->drm.struct_mutex);
 		if (err)
 			goto err_unpin;
 
diff --git a/drivers/gpu/drm/i915/gt/selftest_context.c b/drivers/gpu/drm/i915/gt/selftest_context.c
index 1420533e8fd5..883739354b07 100644
--- a/drivers/gpu/drm/i915/gt/selftest_context.c
+++ b/drivers/gpu/drm/i915/gt/selftest_context.c
@@ -312,7 +312,7 @@ static int live_active_context(void *arg)
 		if (err)
 			break;
 
-		err = igt_flush_test(gt->i915, I915_WAIT_LOCKED);
+		err = igt_flush_test(gt->i915);
 		if (err)
 			break;
 	}
@@ -425,7 +425,7 @@ static int live_remote_context(void *arg)
 		if (err)
 			break;
 
-		err = igt_flush_test(gt->i915, I915_WAIT_LOCKED);
+		err = igt_flush_test(gt->i915);
 		if (err)
 			break;
 	}
diff --git a/drivers/gpu/drm/i915/gt/selftest_hangcheck.c b/drivers/gpu/drm/i915/gt/selftest_hangcheck.c
index e53eea1050f8..fc4d02406536 100644
--- a/drivers/gpu/drm/i915/gt/selftest_hangcheck.c
+++ b/drivers/gpu/drm/i915/gt/selftest_hangcheck.c
@@ -58,7 +58,9 @@ static int hang_init(struct hang *h, struct intel_gt *gt)
 	memset(h, 0, sizeof(*h));
 	h->gt = gt;
 
+	mutex_lock(&gt->i915->drm.struct_mutex);
 	h->ctx = kernel_context(gt->i915);
+	mutex_unlock(&gt->i915->drm.struct_mutex);
 	if (IS_ERR(h->ctx))
 		return PTR_ERR(h->ctx);
 
@@ -285,7 +287,7 @@ static void hang_fini(struct hang *h)
 
 	kernel_context_close(h->ctx);
 
-	igt_flush_test(h->gt->i915, I915_WAIT_LOCKED);
+	igt_flush_test(h->gt->i915);
 }
 
 static bool wait_until_running(struct hang *h, struct i915_request *rq)
@@ -309,10 +311,9 @@ static int igt_hang_sanitycheck(void *arg)
 
 	/* Basic check that we can execute our hanging batch */
 
-	mutex_lock(&gt->i915->drm.struct_mutex);
 	err = hang_init(&h, gt);
 	if (err)
-		goto unlock;
+		return err;
 
 	for_each_engine(engine, gt->i915, id) {
 		struct intel_wedge_me w;
@@ -355,8 +356,6 @@ static int igt_hang_sanitycheck(void *arg)
 
 fini:
 	hang_fini(&h);
-unlock:
-	mutex_unlock(&gt->i915->drm.struct_mutex);
 	return err;
 }
 
@@ -395,8 +394,6 @@ static int igt_reset_nop(void *arg)
 	reset_count = i915_reset_count(global);
 	count = 0;
 	do {
-		mutex_lock(&gt->i915->drm.struct_mutex);
-
 		for_each_engine(engine, gt->i915, id) {
 			int i;
 
@@ -417,7 +414,6 @@ static int igt_reset_nop(void *arg)
 		intel_gt_reset(gt, ALL_ENGINES, NULL);
 		igt_global_reset_unlock(gt);
 
-		mutex_unlock(&gt->i915->drm.struct_mutex);
 		if (intel_gt_is_wedged(gt)) {
 			err = -EIO;
 			break;
@@ -429,16 +425,13 @@ static int igt_reset_nop(void *arg)
 			break;
 		}
 
-		err = igt_flush_test(gt->i915, 0);
+		err = igt_flush_test(gt->i915);
 		if (err)
 			break;
 	} while (time_before(jiffies, end_time));
 	pr_info("%s: %d resets\n", __func__, count);
 
-	mutex_lock(&gt->i915->drm.struct_mutex);
-	err = igt_flush_test(gt->i915, I915_WAIT_LOCKED);
-	mutex_unlock(&gt->i915->drm.struct_mutex);
-
+	err = igt_flush_test(gt->i915);
 out:
 	mock_file_free(gt->i915, file);
 	if (intel_gt_is_wedged(gt))
@@ -494,7 +487,6 @@ static int igt_reset_nop_engine(void *arg)
 				break;
 			}
 
-			mutex_lock(&gt->i915->drm.struct_mutex);
 			for (i = 0; i < 16; i++) {
 				struct i915_request *rq;
 
@@ -507,7 +499,6 @@ static int igt_reset_nop_engine(void *arg)
 				i915_request_add(rq);
 			}
 			err = intel_engine_reset(engine, NULL);
-			mutex_unlock(&gt->i915->drm.struct_mutex);
 			if (err) {
 				pr_err("i915_reset_engine failed\n");
 				break;
@@ -533,15 +524,12 @@ static int igt_reset_nop_engine(void *arg)
 		if (err)
 			break;
 
-		err = igt_flush_test(gt->i915, 0);
+		err = igt_flush_test(gt->i915);
 		if (err)
 			break;
 	}
 
-	mutex_lock(&gt->i915->drm.struct_mutex);
-	err = igt_flush_test(gt->i915, I915_WAIT_LOCKED);
-	mutex_unlock(&gt->i915->drm.struct_mutex);
-
+	err = igt_flush_test(gt->i915);
 out:
 	mock_file_free(gt->i915, file);
 	if (intel_gt_is_wedged(gt))
@@ -563,9 +551,7 @@ static int __igt_reset_engine(struct intel_gt *gt, bool active)
 		return 0;
 
 	if (active) {
-		mutex_lock(&gt->i915->drm.struct_mutex);
 		err = hang_init(&h, gt);
-		mutex_unlock(&gt->i915->drm.struct_mutex);
 		if (err)
 			return err;
 	}
@@ -593,17 +579,14 @@ static int __igt_reset_engine(struct intel_gt *gt, bool active)
 			if (active) {
 				struct i915_request *rq;
 
-				mutex_lock(&gt->i915->drm.struct_mutex);
 				rq = hang_create_request(&h, engine);
 				if (IS_ERR(rq)) {
 					err = PTR_ERR(rq);
-					mutex_unlock(&gt->i915->drm.struct_mutex);
 					break;
 				}
 
 				i915_request_get(rq);
 				i915_request_add(rq);
-				mutex_unlock(&gt->i915->drm.struct_mutex);
 
 				if (!wait_until_running(&h, rq)) {
 					struct drm_printer p = drm_info_printer(gt->i915->drm.dev);
@@ -647,7 +630,7 @@ static int __igt_reset_engine(struct intel_gt *gt, bool active)
 		if (err)
 			break;
 
-		err = igt_flush_test(gt->i915, 0);
+		err = igt_flush_test(gt->i915);
 		if (err)
 			break;
 	}
@@ -655,11 +638,8 @@ static int __igt_reset_engine(struct intel_gt *gt, bool active)
 	if (intel_gt_is_wedged(gt))
 		err = -EIO;
 
-	if (active) {
-		mutex_lock(&gt->i915->drm.struct_mutex);
+	if (active)
 		hang_fini(&h);
-		mutex_unlock(&gt->i915->drm.struct_mutex);
-	}
 
 	return err;
 }
@@ -741,10 +721,8 @@ static int active_engine(void *data)
 		struct i915_request *old = rq[idx];
 		struct i915_request *new;
 
-		mutex_lock(&engine->i915->drm.struct_mutex);
 		new = igt_request_alloc(ctx[idx], engine);
 		if (IS_ERR(new)) {
-			mutex_unlock(&engine->i915->drm.struct_mutex);
 			err = PTR_ERR(new);
 			break;
 		}
@@ -755,7 +733,6 @@ static int active_engine(void *data)
 
 		rq[idx] = i915_request_get(new);
 		i915_request_add(new);
-		mutex_unlock(&engine->i915->drm.struct_mutex);
 
 		err = active_request_put(old);
 		if (err)
@@ -795,9 +772,7 @@ static int __igt_reset_engines(struct intel_gt *gt,
 		return 0;
 
 	if (flags & TEST_ACTIVE) {
-		mutex_lock(&gt->i915->drm.struct_mutex);
 		err = hang_init(&h, gt);
-		mutex_unlock(&gt->i915->drm.struct_mutex);
 		if (err)
 			return err;
 
@@ -855,17 +830,14 @@ static int __igt_reset_engines(struct intel_gt *gt,
 			struct i915_request *rq = NULL;
 
 			if (flags & TEST_ACTIVE) {
-				mutex_lock(&gt->i915->drm.struct_mutex);
 				rq = hang_create_request(&h, engine);
 				if (IS_ERR(rq)) {
 					err = PTR_ERR(rq);
-					mutex_unlock(&gt->i915->drm.struct_mutex);
 					break;
 				}
 
 				i915_request_get(rq);
 				i915_request_add(rq);
-				mutex_unlock(&gt->i915->drm.struct_mutex);
 
 				if (!wait_until_running(&h, rq)) {
 					struct drm_printer p = drm_info_printer(gt->i915->drm.dev);
@@ -977,9 +949,7 @@ static int __igt_reset_engines(struct intel_gt *gt,
 		if (err)
 			break;
 
-		mutex_lock(&gt->i915->drm.struct_mutex);
-		err = igt_flush_test(gt->i915, I915_WAIT_LOCKED);
-		mutex_unlock(&gt->i915->drm.struct_mutex);
+		err = igt_flush_test(gt->i915);
 		if (err)
 			break;
 	}
@@ -987,11 +957,8 @@ static int __igt_reset_engines(struct intel_gt *gt,
 	if (intel_gt_is_wedged(gt))
 		err = -EIO;
 
-	if (flags & TEST_ACTIVE) {
-		mutex_lock(&gt->i915->drm.struct_mutex);
+	if (flags & TEST_ACTIVE)
 		hang_fini(&h);
-		mutex_unlock(&gt->i915->drm.struct_mutex);
-	}
 
 	return err;
 }
@@ -1061,7 +1028,6 @@ static int igt_reset_wait(void *arg)
 
 	igt_global_reset_lock(gt);
 
-	mutex_lock(&gt->i915->drm.struct_mutex);
 	err = hang_init(&h, gt);
 	if (err)
 		goto unlock;
@@ -1109,7 +1075,6 @@ static int igt_reset_wait(void *arg)
 fini:
 	hang_fini(&h);
 unlock:
-	mutex_unlock(&gt->i915->drm.struct_mutex);
 	igt_global_reset_unlock(gt);
 
 	if (intel_gt_is_wedged(gt))
@@ -1189,10 +1154,9 @@ static int __igt_reset_evict_vma(struct intel_gt *gt,
 
 	/* Check that we can recover an unbind stuck on a hanging request */
 
-	mutex_lock(&gt->i915->drm.struct_mutex);
 	err = hang_init(&h, gt);
 	if (err)
-		goto unlock;
+		return err;
 
 	obj = i915_gem_object_create_internal(gt->i915, SZ_1M);
 	if (IS_ERR(obj)) {
@@ -1255,8 +1219,6 @@ static int __igt_reset_evict_vma(struct intel_gt *gt,
 	if (err)
 		goto out_rq;
 
-	mutex_unlock(&gt->i915->drm.struct_mutex);
-
 	if (!wait_until_running(&h, rq)) {
 		struct drm_printer p = drm_info_printer(gt->i915->drm.dev);
 
@@ -1305,16 +1267,12 @@ static int __igt_reset_evict_vma(struct intel_gt *gt,
 		put_task_struct(tsk);
 	}
 
-	mutex_lock(&gt->i915->drm.struct_mutex);
 out_rq:
 	i915_request_put(rq);
 out_obj:
 	i915_gem_object_put(obj);
 fini:
 	hang_fini(&h);
-unlock:
-	mutex_unlock(&gt->i915->drm.struct_mutex);
-
 	if (intel_gt_is_wedged(gt))
 		return -EIO;
 
@@ -1396,7 +1354,6 @@ static int igt_reset_queue(void *arg)
 
 	igt_global_reset_lock(gt);
 
-	mutex_lock(&gt->i915->drm.struct_mutex);
 	err = hang_init(&h, gt);
 	if (err)
 		goto unlock;
@@ -1511,7 +1468,7 @@ static int igt_reset_queue(void *arg)
 
 		i915_request_put(prev);
 
-		err = igt_flush_test(gt->i915, I915_WAIT_LOCKED);
+		err = igt_flush_test(gt->i915);
 		if (err)
 			break;
 	}
@@ -1519,7 +1476,6 @@ static int igt_reset_queue(void *arg)
 fini:
 	hang_fini(&h);
 unlock:
-	mutex_unlock(&gt->i915->drm.struct_mutex);
 	igt_global_reset_unlock(gt);
 
 	if (intel_gt_is_wedged(gt))
@@ -1546,11 +1502,9 @@ static int igt_handle_error(void *arg)
 	if (!engine || !intel_engine_can_store_dword(engine))
 		return 0;
 
-	mutex_lock(&gt->i915->drm.struct_mutex);
-
 	err = hang_init(&h, gt);
 	if (err)
-		goto err_unlock;
+		return err;
 
 	rq = hang_create_request(&h, engine);
 	if (IS_ERR(rq)) {
@@ -1574,8 +1528,6 @@ static int igt_handle_error(void *arg)
 		goto err_request;
 	}
 
-	mutex_unlock(&gt->i915->drm.struct_mutex);
-
 	/* Temporarily disable error capture */
 	error = xchg(&global->first_error, (void *)-1);
 
@@ -1583,8 +1535,6 @@ static int igt_handle_error(void *arg)
 
 	xchg(&global->first_error, error);
 
-	mutex_lock(&gt->i915->drm.struct_mutex);
-
 	if (rq->fence.error != -EIO) {
 		pr_err("Guilty request not identified!\n");
 		err = -EINVAL;
@@ -1595,8 +1545,6 @@ static int igt_handle_error(void *arg)
 	i915_request_put(rq);
 err_fini:
 	hang_fini(&h);
-err_unlock:
-	mutex_unlock(&gt->i915->drm.struct_mutex);
 	return err;
 }
 
@@ -1689,7 +1637,6 @@ static int igt_reset_engines_atomic(void *arg)
 		return 0;
 
 	igt_global_reset_lock(gt);
-	mutex_lock(&gt->i915->drm.struct_mutex);
 
 	/* Flush any requests before we get started and check basics */
 	if (!igt_force_reset(gt))
@@ -1709,9 +1656,7 @@ static int igt_reset_engines_atomic(void *arg)
 out:
 	/* As we poke around the guts, do a full reset before continuing. */
 	igt_force_reset(gt);
-
 unlock:
-	mutex_unlock(&gt->i915->drm.struct_mutex);
 	igt_global_reset_unlock(gt);
 
 	return err;
@@ -1751,10 +1696,6 @@ int intel_hangcheck_live_selftests(struct drm_i915_private *i915)
 
 	err = intel_gt_live_subtests(tests, gt);
 
-	mutex_lock(&gt->i915->drm.struct_mutex);
-	igt_flush_test(gt->i915, I915_WAIT_LOCKED);
-	mutex_unlock(&gt->i915->drm.struct_mutex);
-
 	i915_modparams.enable_hangcheck = saved_hangcheck;
 	intel_runtime_pm_put(&gt->i915->runtime_pm, wakeref);
 
diff --git a/drivers/gpu/drm/i915/gt/selftest_lrc.c b/drivers/gpu/drm/i915/gt/selftest_lrc.c
index aca1b3a9c5de..222a7375c787 100644
--- a/drivers/gpu/drm/i915/gt/selftest_lrc.c
+++ b/drivers/gpu/drm/i915/gt/selftest_lrc.c
@@ -61,7 +61,7 @@ static int live_sanitycheck(void *arg)
 		}
 
 		igt_spinner_end(&spin);
-		if (igt_flush_test(i915, I915_WAIT_LOCKED)) {
+		if (igt_flush_test(i915)) {
 			err = -EIO;
 			goto err_ctx;
 		}
@@ -206,8 +206,7 @@ slice_semaphore_queue(struct intel_engine_cs *outer,
 	if (err)
 		goto out;
 
-	if (i915_request_wait(head,
-			      I915_WAIT_LOCKED,
+	if (i915_request_wait(head, 0,
 			      2 * RUNTIME_INFO(outer->i915)->num_engines * (count + 2) * (count + 3)) < 0) {
 		pr_err("Failed to slice along semaphore chain of length (%d, %d)!\n",
 		       count, n);
@@ -279,7 +278,7 @@ static int live_timeslice_preempt(void *arg)
 			if (err)
 				goto err_pin;
 
-			if (igt_flush_test(i915, I915_WAIT_LOCKED)) {
+			if (igt_flush_test(i915)) {
 				err = -EIO;
 				goto err_pin;
 			}
@@ -832,7 +831,7 @@ static int live_nopreempt(void *arg)
 			goto err_wedged;
 		}
 
-		if (igt_flush_test(i915, I915_WAIT_LOCKED))
+		if (igt_flush_test(i915))
 			goto err_wedged;
 	}
 
@@ -948,7 +947,7 @@ static int live_suppress_self_preempt(void *arg)
 			goto err_client_b;
 		}
 
-		if (igt_flush_test(i915, I915_WAIT_LOCKED))
+		if (igt_flush_test(i915))
 			goto err_wedged;
 	}
 
@@ -1109,7 +1108,7 @@ static int live_suppress_wait_preempt(void *arg)
 			for (i = 0; i < ARRAY_SIZE(client); i++)
 				igt_spinner_end(&client[i].spin);
 
-			if (igt_flush_test(i915, I915_WAIT_LOCKED))
+			if (igt_flush_test(i915))
 				goto err_wedged;
 
 			if (engine->execlists.preempt_hang.count) {
@@ -1388,7 +1387,7 @@ static int live_preempt_hang(void *arg)
 
 		igt_spinner_end(&spin_hi);
 		igt_spinner_end(&spin_lo);
-		if (igt_flush_test(i915, I915_WAIT_LOCKED)) {
+		if (igt_flush_test(i915)) {
 			err = -EIO;
 			goto err_ctx_lo;
 		}
@@ -1785,7 +1784,7 @@ static int nop_virtual_engine(struct drm_i915_private *i915,
 		prime, div64_u64(ktime_to_ns(times[1]), prime));
 
 out:
-	if (igt_flush_test(i915, I915_WAIT_LOCKED))
+	if (igt_flush_test(i915))
 		err = -EIO;
 
 	for (nc = 0; nc < nctx; nc++) {
@@ -1930,7 +1929,7 @@ static int mask_virtual_engine(struct drm_i915_private *i915,
 		goto out;
 
 out:
-	if (igt_flush_test(i915, I915_WAIT_LOCKED))
+	if (igt_flush_test(i915))
 		err = -EIO;
 
 	for (n = 0; n < nsibling; n++)
@@ -2108,7 +2107,7 @@ static int bond_virtual_engine(struct drm_i915_private *i915,
 out:
 	for (n = 0; !IS_ERR(rq[n]); n++)
 		i915_request_put(rq[n]);
-	if (igt_flush_test(i915, I915_WAIT_LOCKED))
+	if (igt_flush_test(i915))
 		err = -EIO;
 
 	kernel_context_close(ctx);
diff --git a/drivers/gpu/drm/i915/gt/selftest_timeline.c b/drivers/gpu/drm/i915/gt/selftest_timeline.c
index 321481403165..16abfabf08c7 100644
--- a/drivers/gpu/drm/i915/gt/selftest_timeline.c
+++ b/drivers/gpu/drm/i915/gt/selftest_timeline.c
@@ -6,7 +6,7 @@
 
 #include <linux/prime_numbers.h>
 
-#include "gem/i915_gem_pm.h"
+#include "intel_engine_pm.h"
 #include "intel_gt.h"
 
 #include "../selftests/i915_random.h"
@@ -136,7 +136,6 @@ static int mock_hwsp_freelist(void *arg)
 		goto err_put;
 	}
 
-	mutex_lock(&state.i915->drm.struct_mutex);
 	for (p = phases; p->name; p++) {
 		pr_debug("%s(%s)\n", __func__, p->name);
 		for_each_prime_number_from(na, 1, 2 * CACHELINES_PER_PAGE) {
@@ -149,7 +148,6 @@ static int mock_hwsp_freelist(void *arg)
 out:
 	for (na = 0; na < state.max; na++)
 		__mock_hwsp_record(&state, na, NULL);
-	mutex_unlock(&state.i915->drm.struct_mutex);
 	kfree(state.history);
 err_put:
 	drm_dev_put(&state.i915->drm);
@@ -449,8 +447,6 @@ tl_write(struct intel_timeline *tl, struct intel_engine_cs *engine, u32 value)
 	struct i915_request *rq;
 	int err;
 
-	lockdep_assert_held(&tl->gt->i915->drm.struct_mutex); /* lazy rq refs */
-
 	err = intel_timeline_pin(tl);
 	if (err) {
 		rq = ERR_PTR(err);
@@ -461,10 +457,14 @@ tl_write(struct intel_timeline *tl, struct intel_engine_cs *engine, u32 value)
 	if (IS_ERR(rq))
 		goto out_unpin;
 
+	i915_request_get(rq);
+
 	err = emit_ggtt_store_dw(rq, tl->hwsp_offset, value);
 	i915_request_add(rq);
-	if (err)
+	if (err) {
+		i915_request_put(rq);
 		rq = ERR_PTR(err);
+	}
 
 out_unpin:
 	intel_timeline_unpin(tl);
@@ -500,7 +500,6 @@ static int live_hwsp_engine(void *arg)
 	struct intel_timeline **timelines;
 	struct intel_engine_cs *engine;
 	enum intel_engine_id id;
-	intel_wakeref_t wakeref;
 	unsigned long count, n;
 	int err = 0;
 
@@ -515,14 +514,13 @@ static int live_hwsp_engine(void *arg)
 	if (!timelines)
 		return -ENOMEM;
 
-	mutex_lock(&i915->drm.struct_mutex);
-	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
-
 	count = 0;
 	for_each_engine(engine, i915, id) {
 		if (!intel_engine_can_store_dword(engine))
 			continue;
 
+		intel_engine_pm_get(engine);
+
 		for (n = 0; n < NUM_TIMELINES; n++) {
 			struct intel_timeline *tl;
 			struct i915_request *rq;
@@ -530,22 +528,26 @@ static int live_hwsp_engine(void *arg)
 			tl = checked_intel_timeline_create(i915);
 			if (IS_ERR(tl)) {
 				err = PTR_ERR(tl);
-				goto out;
+				break;
 			}
 
 			rq = tl_write(tl, engine, count);
 			if (IS_ERR(rq)) {
 				intel_timeline_put(tl);
 				err = PTR_ERR(rq);
-				goto out;
+				break;
 			}
 
 			timelines[count++] = tl;
+			i915_request_put(rq);
 		}
+
+		intel_engine_pm_put(engine);
+		if (err)
+			break;
 	}
 
-out:
-	if (igt_flush_test(i915, I915_WAIT_LOCKED))
+	if (igt_flush_test(i915))
 		err = -EIO;
 
 	for (n = 0; n < count; n++) {
@@ -559,11 +561,7 @@ static int live_hwsp_engine(void *arg)
 		intel_timeline_put(tl);
 	}
 
-	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
-	mutex_unlock(&i915->drm.struct_mutex);
-
 	kvfree(timelines);
-
 	return err;
 #undef NUM_TIMELINES
 }
@@ -575,7 +573,6 @@ static int live_hwsp_alternate(void *arg)
 	struct intel_timeline **timelines;
 	struct intel_engine_cs *engine;
 	enum intel_engine_id id;
-	intel_wakeref_t wakeref;
 	unsigned long count, n;
 	int err = 0;
 
@@ -591,9 +588,6 @@ static int live_hwsp_alternate(void *arg)
 	if (!timelines)
 		return -ENOMEM;
 
-	mutex_lock(&i915->drm.struct_mutex);
-	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
-
 	count = 0;
 	for (n = 0; n < NUM_TIMELINES; n++) {
 		for_each_engine(engine, i915, id) {
@@ -605,11 +599,14 @@ static int live_hwsp_alternate(void *arg)
 
 			tl = checked_intel_timeline_create(i915);
 			if (IS_ERR(tl)) {
+				intel_engine_pm_put(engine);
 				err = PTR_ERR(tl);
 				goto out;
 			}
 
+			intel_engine_pm_get(engine);
 			rq = tl_write(tl, engine, count);
+			intel_engine_pm_put(engine);
 			if (IS_ERR(rq)) {
 				intel_timeline_put(tl);
 				err = PTR_ERR(rq);
@@ -617,11 +614,12 @@ static int live_hwsp_alternate(void *arg)
 			}
 
 			timelines[count++] = tl;
+			i915_request_put(rq);
 		}
 	}
 
 out:
-	if (igt_flush_test(i915, I915_WAIT_LOCKED))
+	if (igt_flush_test(i915))
 		err = -EIO;
 
 	for (n = 0; n < count; n++) {
@@ -635,11 +633,7 @@ static int live_hwsp_alternate(void *arg)
 		intel_timeline_put(tl);
 	}
 
-	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
-	mutex_unlock(&i915->drm.struct_mutex);
-
 	kvfree(timelines);
-
 	return err;
 #undef NUM_TIMELINES
 }
@@ -650,7 +644,6 @@ static int live_hwsp_wrap(void *arg)
 	struct intel_engine_cs *engine;
 	struct intel_timeline *tl;
 	enum intel_engine_id id;
-	intel_wakeref_t wakeref;
 	int err = 0;
 
 	/*
@@ -658,14 +651,10 @@ static int live_hwsp_wrap(void *arg)
 	 * foreign GPU references.
 	 */
 
-	mutex_lock(&i915->drm.struct_mutex);
-	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
-
 	tl = intel_timeline_create(&i915->gt, NULL);
-	if (IS_ERR(tl)) {
-		err = PTR_ERR(tl);
-		goto out_rpm;
-	}
+	if (IS_ERR(tl))
+		return PTR_ERR(tl);
+
 	if (!tl->has_initial_breadcrumb || !tl->hwsp_cacheline)
 		goto out_free;
 
@@ -681,7 +670,9 @@ static int live_hwsp_wrap(void *arg)
 		if (!intel_engine_can_store_dword(engine))
 			continue;
 
+		intel_engine_pm_get(engine);
 		rq = i915_request_create(engine->kernel_context);
+		intel_engine_pm_put(engine);
 		if (IS_ERR(rq)) {
 			err = PTR_ERR(rq);
 			goto out;
@@ -747,16 +738,12 @@ static int live_hwsp_wrap(void *arg)
 	}
 
 out:
-	if (igt_flush_test(i915, I915_WAIT_LOCKED))
+	if (igt_flush_test(i915))
 		err = -EIO;
 
 	intel_timeline_unpin(tl);
 out_free:
 	intel_timeline_put(tl);
-out_rpm:
-	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
-	mutex_unlock(&i915->drm.struct_mutex);
-
 	return err;
 }
 
@@ -765,7 +752,6 @@ static int live_hwsp_recycle(void *arg)
 	struct drm_i915_private *i915 = arg;
 	struct intel_engine_cs *engine;
 	enum intel_engine_id id;
-	intel_wakeref_t wakeref;
 	unsigned long count;
 	int err = 0;
 
@@ -775,9 +761,6 @@ static int live_hwsp_recycle(void *arg)
 	 * want to confuse ourselves or the GPU.
 	 */
 
-	mutex_lock(&i915->drm.struct_mutex);
-	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
-
 	count = 0;
 	for_each_engine(engine, i915, id) {
 		IGT_TIMEOUT(end_time);
@@ -785,6 +768,8 @@ static int live_hwsp_recycle(void *arg)
 		if (!intel_engine_can_store_dword(engine))
 			continue;
 
+		intel_engine_pm_get(engine);
+
 		do {
 			struct intel_timeline *tl;
 			struct i915_request *rq;
@@ -792,21 +777,22 @@ static int live_hwsp_recycle(void *arg)
 			tl = checked_intel_timeline_create(i915);
 			if (IS_ERR(tl)) {
 				err = PTR_ERR(tl);
-				goto out;
+				break;
 			}
 
 			rq = tl_write(tl, engine, count);
 			if (IS_ERR(rq)) {
 				intel_timeline_put(tl);
 				err = PTR_ERR(rq);
-				goto out;
+				break;
 			}
 
 			if (i915_request_wait(rq, 0, HZ / 5) < 0) {
 				pr_err("Wait for timeline writes timed out!\n");
+				i915_request_put(rq);
 				intel_timeline_put(tl);
 				err = -EIO;
-				goto out;
+				break;
 			}
 
 			if (*tl->hwsp_seqno != count) {
@@ -815,17 +801,18 @@ static int live_hwsp_recycle(void *arg)
 				err = -EINVAL;
 			}
 
+			i915_request_put(rq);
 			intel_timeline_put(tl);
 			count++;
 
 			if (err)
-				goto out;
+				break;
 		} while (!__igt_timeout(end_time, NULL));
-	}
 
-out:
-	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
-	mutex_unlock(&i915->drm.struct_mutex);
+		intel_engine_pm_put(engine);
+		if (err)
+			break;
+	}
 
 	return err;
 }
diff --git a/drivers/gpu/drm/i915/gt/selftest_workarounds.c b/drivers/gpu/drm/i915/gt/selftest_workarounds.c
index 999a98f00494..06351fefbbf3 100644
--- a/drivers/gpu/drm/i915/gt/selftest_workarounds.c
+++ b/drivers/gpu/drm/i915/gt/selftest_workarounds.c
@@ -676,7 +676,7 @@ static int check_dirty_whitelist(struct i915_gem_context *ctx,
 			break;
 	}
 
-	if (igt_flush_test(ctx->i915, I915_WAIT_LOCKED))
+	if (igt_flush_test(ctx->i915))
 		err = -EIO;
 out_batch:
 	i915_vma_unpin_and_release(&batch, 0);
@@ -1090,7 +1090,7 @@ static int live_isolated_whitelist(void *arg)
 		kernel_context_close(client[i].ctx);
 	}
 
-	if (igt_flush_test(i915, I915_WAIT_LOCKED))
+	if (igt_flush_test(i915))
 		err = -EIO;
 
 	return err;
@@ -1248,7 +1248,7 @@ live_engine_reset_workarounds(void *arg)
 	igt_global_reset_unlock(&i915->gt);
 	kernel_context_close(ctx);
 
-	igt_flush_test(i915, I915_WAIT_LOCKED);
+	igt_flush_test(i915);
 
 	return ret;
 }
diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
index 30b0b592e20d..55f0fc03aa3e 100644
--- a/drivers/gpu/drm/i915/i915_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
@@ -3601,6 +3601,7 @@ static int
 i915_drop_caches_set(void *data, u64 val)
 {
 	struct drm_i915_private *i915 = data;
+	int ret;
 
 	DRM_DEBUG("Dropping caches: 0x%08llx [0x%08llx]\n",
 		  val, val & DROP_ALL);
@@ -3610,40 +3611,21 @@ i915_drop_caches_set(void *data, u64 val)
 		     I915_IDLE_ENGINES_TIMEOUT))
 		intel_gt_set_wedged(&i915->gt);
 
-	/* No need to check and wait for gpu resets, only libdrm auto-restarts
-	 * on ioctls on -EAGAIN. */
-	if (val & (DROP_ACTIVE | DROP_IDLE | DROP_RETIRE | DROP_RESET_SEQNO)) {
-		int ret;
+	if (val & DROP_RETIRE)
+		i915_retire_requests(i915);
 
-		ret = mutex_lock_interruptible(&i915->drm.struct_mutex);
+	if (val & (DROP_IDLE | DROP_ACTIVE)) {
+		ret = i915_gem_wait_for_idle(i915,
+					     I915_WAIT_INTERRUPTIBLE,
+					     MAX_SCHEDULE_TIMEOUT);
 		if (ret)
 			return ret;
+	}
 
-		/*
-		 * To finish the flush of the idle_worker, we must complete
-		 * the switch-to-kernel-context, which requires a double
-		 * pass through wait_for_idle: first queues the switch,
-		 * second waits for the switch.
-		 */
-		if (ret == 0 && val & (DROP_IDLE | DROP_ACTIVE))
-			ret = i915_gem_wait_for_idle(i915,
-						     I915_WAIT_INTERRUPTIBLE |
-						     I915_WAIT_LOCKED,
-						     MAX_SCHEDULE_TIMEOUT);
-
-		if (ret == 0 && val & DROP_IDLE)
-			ret = i915_gem_wait_for_idle(i915,
-						     I915_WAIT_INTERRUPTIBLE |
-						     I915_WAIT_LOCKED,
-						     MAX_SCHEDULE_TIMEOUT);
-
-		if (val & DROP_RETIRE)
-			i915_retire_requests(i915);
-
-		mutex_unlock(&i915->drm.struct_mutex);
-
-		if (ret == 0 && val & DROP_IDLE)
-			ret = intel_gt_pm_wait_for_idle(&i915->gt);
+	if (val & DROP_IDLE) {
+		ret = intel_gt_pm_wait_for_idle(&i915->gt);
+		if (ret)
+			return ret;
 	}
 
 	if (val & DROP_RESET_ACTIVE && intel_gt_terminally_wedged(&i915->gt))
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 1aadab1cdd24..225fd22af858 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -951,19 +951,16 @@ int i915_gem_wait_for_idle(struct drm_i915_private *i915,
 	if (!intel_gt_pm_is_awake(&i915->gt))
 		return 0;
 
-	GEM_TRACE("flags=%x (%s), timeout=%ld%s\n",
-		  flags, flags & I915_WAIT_LOCKED ? "locked" : "unlocked",
-		  timeout, timeout == MAX_SCHEDULE_TIMEOUT ? " (forever)" : "");
-
-	timeout = wait_for_timelines(i915, flags, timeout);
-	if (timeout < 0)
-		return timeout;
+	do {
+		timeout = wait_for_timelines(i915, flags, timeout);
+		if (timeout < 0)
+			return timeout;
 
-	if (flags & I915_WAIT_LOCKED) {
-		lockdep_assert_held(&i915->drm.struct_mutex);
+		cond_resched();
+		if (signal_pending(current))
+			return -EINTR;
 
-		i915_retire_requests(i915);
-	}
+	} while (i915_retire_requests(i915));
 
 	return 0;
 }
diff --git a/drivers/gpu/drm/i915/i915_request.h b/drivers/gpu/drm/i915/i915_request.h
index 3251d2bdbeea..57a2193c64d1 100644
--- a/drivers/gpu/drm/i915/i915_request.h
+++ b/drivers/gpu/drm/i915/i915_request.h
@@ -308,10 +308,9 @@ long i915_request_wait(struct i915_request *rq,
 		       long timeout)
 	__attribute__((nonnull(1)));
 #define I915_WAIT_INTERRUPTIBLE	BIT(0)
-#define I915_WAIT_LOCKED	BIT(1) /* struct_mutex held, handle GPU reset */
-#define I915_WAIT_PRIORITY	BIT(2) /* small priority bump for the request */
-#define I915_WAIT_ALL		BIT(3) /* used by i915_gem_object_wait() */
-#define I915_WAIT_FOR_IDLE_BOOST BIT(4)
+#define I915_WAIT_PRIORITY	BIT(1) /* small priority bump for the request */
+#define I915_WAIT_ALL		BIT(2) /* used by i915_gem_object_wait() */
+#define I915_WAIT_FOR_IDLE_BOOST BIT(3)
 
 static inline bool i915_request_signaled(const struct i915_request *rq)
 {
diff --git a/drivers/gpu/drm/i915/selftests/i915_active.c b/drivers/gpu/drm/i915/selftests/i915_active.c
index af5827aac7b2..ff1337e34522 100644
--- a/drivers/gpu/drm/i915/selftests/i915_active.c
+++ b/drivers/gpu/drm/i915/selftests/i915_active.c
@@ -164,10 +164,8 @@ static int live_active_wait(void *arg)
 
 	__live_put(active);
 
-	mutex_lock(&i915->drm.struct_mutex);
-	if (igt_flush_test(i915, I915_WAIT_LOCKED))
+	if (igt_flush_test(i915))
 		err = -EIO;
-	mutex_unlock(&i915->drm.struct_mutex);
 
 	return err;
 }
@@ -185,10 +183,8 @@ static int live_active_retire(void *arg)
 		return PTR_ERR(active);
 
 	/* waits for & retires all requests */
-	mutex_lock(&i915->drm.struct_mutex);
-	if (igt_flush_test(i915, I915_WAIT_LOCKED))
+	if (igt_flush_test(i915))
 		err = -EIO;
-	mutex_unlock(&i915->drm.struct_mutex);
 
 	if (!READ_ONCE(active->retired)) {
 		pr_err("i915_active not retired after flushing!\n");
diff --git a/drivers/gpu/drm/i915/selftests/i915_gem_evict.c b/drivers/gpu/drm/i915/selftests/i915_gem_evict.c
index ba6064147173..42139db0d69c 100644
--- a/drivers/gpu/drm/i915/selftests/i915_gem_evict.c
+++ b/drivers/gpu/drm/i915/selftests/i915_gem_evict.c
@@ -521,7 +521,7 @@ static int igt_evict_contexts(void *arg)
 
 	mutex_lock(&i915->ggtt.vm.mutex);
 out_locked:
-	if (igt_flush_test(i915, I915_WAIT_LOCKED))
+	if (igt_flush_test(i915))
 		err = -EIO;
 	while (reserved) {
 		struct reserved *next = reserved->next;
diff --git a/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c b/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
index 4235fa401956..729a53eb6244 100644
--- a/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
+++ b/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
@@ -1709,12 +1709,8 @@ int i915_gem_gtt_mock_selftests(void)
 
 	err = i915_subtests(tests, ggtt);
 
-	mutex_lock(&i915->drm.struct_mutex);
 	mock_device_flush(i915);
-	mutex_unlock(&i915->drm.struct_mutex);
-
 	i915_gem_drain_freed_objects(i915);
-
 	mock_fini_ggtt(ggtt);
 	kfree(ggtt);
 out_put:
diff --git a/drivers/gpu/drm/i915/selftests/i915_request.c b/drivers/gpu/drm/i915/selftests/i915_request.c
index b3688543ed7d..d046395845da 100644
--- a/drivers/gpu/drm/i915/selftests/i915_request.c
+++ b/drivers/gpu/drm/i915/selftests/i915_request.c
@@ -41,21 +41,16 @@ static int igt_add_request(void *arg)
 {
 	struct drm_i915_private *i915 = arg;
 	struct i915_request *request;
-	int err = -ENOMEM;
 
 	/* Basic preliminary test to create a request and let it loose! */
 
-	mutex_lock(&i915->drm.struct_mutex);
 	request = mock_request(i915->engine[RCS0]->kernel_context, HZ / 10);
 	if (!request)
-		goto out_unlock;
+		return -ENOMEM;
 
 	i915_request_add(request);
 
-	err = 0;
-out_unlock:
-	mutex_unlock(&i915->drm.struct_mutex);
-	return err;
+	return 0;
 }
 
 static int igt_wait_request(void *arg)
@@ -67,12 +62,10 @@ static int igt_wait_request(void *arg)
 
 	/* Submit a request, then wait upon it */
 
-	mutex_lock(&i915->drm.struct_mutex);
 	request = mock_request(i915->engine[RCS0]->kernel_context, T);
-	if (!request) {
-		err = -ENOMEM;
-		goto out_unlock;
-	}
+	if (!request)
+		return -ENOMEM;
+
 	i915_request_get(request);
 
 	if (i915_request_wait(request, 0, 0) != -ETIME) {
@@ -125,9 +118,7 @@ static int igt_wait_request(void *arg)
 	err = 0;
 out_request:
 	i915_request_put(request);
-out_unlock:
 	mock_device_flush(i915);
-	mutex_unlock(&i915->drm.struct_mutex);
 	return err;
 }
 
@@ -140,52 +131,45 @@ static int igt_fence_wait(void *arg)
 
 	/* Submit a request, treat it as a fence and wait upon it */
 
-	mutex_lock(&i915->drm.struct_mutex);
 	request = mock_request(i915->engine[RCS0]->kernel_context, T);
-	if (!request) {
-		err = -ENOMEM;
-		goto out_locked;
-	}
+	if (!request)
+		return -ENOMEM;
 
 	if (dma_fence_wait_timeout(&request->fence, false, T) != -ETIME) {
 		pr_err("fence wait success before submit (expected timeout)!\n");
-		goto out_locked;
+		goto out;
 	}
 
 	i915_request_add(request);
-	mutex_unlock(&i915->drm.struct_mutex);
 
 	if (dma_fence_is_signaled(&request->fence)) {
 		pr_err("fence signaled immediately!\n");
-		goto out_device;
+		goto out;
 	}
 
 	if (dma_fence_wait_timeout(&request->fence, false, T / 2) != -ETIME) {
 		pr_err("fence wait success after submit (expected timeout)!\n");
-		goto out_device;
+		goto out;
 	}
 
 	if (dma_fence_wait_timeout(&request->fence, false, T) <= 0) {
 		pr_err("fence wait timed out (expected success)!\n");
-		goto out_device;
+		goto out;
 	}
 
 	if (!dma_fence_is_signaled(&request->fence)) {
 		pr_err("fence unsignaled after waiting!\n");
-		goto out_device;
+		goto out;
 	}
 
 	if (dma_fence_wait_timeout(&request->fence, false, T) <= 0) {
 		pr_err("fence wait timed out when complete (expected success)!\n");
-		goto out_device;
+		goto out;
 	}
 
 	err = 0;
-out_device:
-	mutex_lock(&i915->drm.struct_mutex);
-out_locked:
+out:
 	mock_device_flush(i915);
-	mutex_unlock(&i915->drm.struct_mutex);
 	return err;
 }
 
@@ -199,6 +183,8 @@ static int igt_request_rewind(void *arg)
 
 	mutex_lock(&i915->drm.struct_mutex);
 	ctx[0] = mock_context(i915, "A");
+	mutex_unlock(&i915->drm.struct_mutex);
+
 	ce = i915_gem_context_get_engine(ctx[0], RCS0);
 	GEM_BUG_ON(IS_ERR(ce));
 	request = mock_request(ce, 2 * HZ);
@@ -211,7 +197,10 @@ static int igt_request_rewind(void *arg)
 	i915_request_get(request);
 	i915_request_add(request);
 
+	mutex_lock(&i915->drm.struct_mutex);
 	ctx[1] = mock_context(i915, "B");
+	mutex_unlock(&i915->drm.struct_mutex);
+
 	ce = i915_gem_context_get_engine(ctx[1], RCS0);
 	GEM_BUG_ON(IS_ERR(ce));
 	vip = mock_request(ce, 0);
@@ -233,7 +222,6 @@ static int igt_request_rewind(void *arg)
 	request->engine->submit_request(request);
 	rcu_read_unlock();
 
-	mutex_unlock(&i915->drm.struct_mutex);
 
 	if (i915_request_wait(vip, 0, HZ) == -ETIME) {
 		pr_err("timed out waiting for high priority request\n");
@@ -248,14 +236,12 @@ static int igt_request_rewind(void *arg)
 	err = 0;
 err:
 	i915_request_put(vip);
-	mutex_lock(&i915->drm.struct_mutex);
 err_context_1:
 	mock_context_close(ctx[1]);
 	i915_request_put(request);
 err_context_0:
 	mock_context_close(ctx[0]);
 	mock_device_flush(i915);
-	mutex_unlock(&i915->drm.struct_mutex);
 	return err;
 }
 
@@ -282,7 +268,6 @@ __live_request_alloc(struct intel_context *ce)
 static int __igt_breadcrumbs_smoketest(void *arg)
 {
 	struct smoketest *t = arg;
-	struct mutex * const BKL = &t->engine->i915->drm.struct_mutex;
 	const unsigned int max_batch = min(t->ncontexts, t->max_batch) - 1;
 	const unsigned int total = 4 * t->ncontexts + 1;
 	unsigned int num_waits = 0, num_fences = 0;
@@ -337,14 +322,11 @@ static int __igt_breadcrumbs_smoketest(void *arg)
 			struct i915_request *rq;
 			struct intel_context *ce;
 
-			mutex_lock(BKL);
-
 			ce = i915_gem_context_get_engine(ctx, t->engine->legacy_idx);
 			GEM_BUG_ON(IS_ERR(ce));
 			rq = t->request_alloc(ce);
 			intel_context_put(ce);
 			if (IS_ERR(rq)) {
-				mutex_unlock(BKL);
 				err = PTR_ERR(rq);
 				count = n;
 				break;
@@ -357,8 +339,6 @@ static int __igt_breadcrumbs_smoketest(void *arg)
 			requests[n] = i915_request_get(rq);
 			i915_request_add(rq);
 
-			mutex_unlock(BKL);
-
 			if (err >= 0)
 				err = i915_sw_fence_await_dma_fence(wait,
 								    &rq->fence,
@@ -457,15 +437,15 @@ static int mock_breadcrumbs_smoketest(void *arg)
 		goto out_threads;
 	}
 
-	mutex_lock(&t.engine->i915->drm.struct_mutex);
 	for (n = 0; n < t.ncontexts; n++) {
+		mutex_lock(&t.engine->i915->drm.struct_mutex);
 		t.contexts[n] = mock_context(t.engine->i915, "mock");
+		mutex_unlock(&t.engine->i915->drm.struct_mutex);
 		if (!t.contexts[n]) {
 			ret = -ENOMEM;
 			goto out_contexts;
 		}
 	}
-	mutex_unlock(&t.engine->i915->drm.struct_mutex);
 
 	for (n = 0; n < ncpus; n++) {
 		threads[n] = kthread_run(__igt_breadcrumbs_smoketest,
@@ -495,18 +475,15 @@ static int mock_breadcrumbs_smoketest(void *arg)
 		atomic_long_read(&t.num_fences),
 		ncpus);
 
-	mutex_lock(&t.engine->i915->drm.struct_mutex);
 out_contexts:
 	for (n = 0; n < t.ncontexts; n++) {
 		if (!t.contexts[n])
 			break;
 		mock_context_close(t.contexts[n]);
 	}
-	mutex_unlock(&t.engine->i915->drm.struct_mutex);
 	kfree(t.contexts);
 out_threads:
 	kfree(threads);
-
 	return ret;
 }
 
@@ -539,7 +516,6 @@ static int live_nop_request(void *arg)
 {
 	struct drm_i915_private *i915 = arg;
 	struct intel_engine_cs *engine;
-	intel_wakeref_t wakeref;
 	struct igt_live_test t;
 	unsigned int id;
 	int err = -ENODEV;
@@ -549,9 +525,6 @@ static int live_nop_request(void *arg)
 	 * the overhead of submitting requests to the hardware.
 	 */
 
-	mutex_lock(&i915->drm.struct_mutex);
-	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
-
 	for_each_engine(engine, i915, id) {
 		struct i915_request *request = NULL;
 		unsigned long n, prime;
@@ -560,17 +533,15 @@ static int live_nop_request(void *arg)
 
 		err = igt_live_test_begin(&t, i915, __func__, engine->name);
 		if (err)
-			goto out_unlock;
+			return err;
 
 		for_each_prime_number_from(prime, 1, 8192) {
 			times[1] = ktime_get_raw();
 
 			for (n = 0; n < prime; n++) {
 				request = i915_request_create(engine->kernel_context);
-				if (IS_ERR(request)) {
-					err = PTR_ERR(request);
-					goto out_unlock;
-				}
+				if (IS_ERR(request))
+					return PTR_ERR(request);
 
 				/* This space is left intentionally blank.
 				 *
@@ -599,7 +570,7 @@ static int live_nop_request(void *arg)
 
 		err = igt_live_test_end(&t);
 		if (err)
-			goto out_unlock;
+			return err;
 
 		pr_info("Request latencies on %s: 1 = %lluns, %lu = %lluns\n",
 			engine->name,
@@ -607,9 +578,6 @@ static int live_nop_request(void *arg)
 			prime, div64_u64(ktime_to_ns(times[1]), prime));
 	}
 
-out_unlock:
-	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
-	mutex_unlock(&i915->drm.struct_mutex);
 	return err;
 }
 
@@ -681,7 +649,6 @@ static int live_empty_request(void *arg)
 {
 	struct drm_i915_private *i915 = arg;
 	struct intel_engine_cs *engine;
-	intel_wakeref_t wakeref;
 	struct igt_live_test t;
 	struct i915_vma *batch;
 	unsigned int id;
@@ -692,14 +659,9 @@ static int live_empty_request(void *arg)
 	 * the overhead of submitting requests to the hardware.
 	 */
 
-	mutex_lock(&i915->drm.struct_mutex);
-	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
-
 	batch = empty_batch(i915);
-	if (IS_ERR(batch)) {
-		err = PTR_ERR(batch);
-		goto out_unlock;
-	}
+	if (IS_ERR(batch))
+		return PTR_ERR(batch);
 
 	for_each_engine(engine, i915, id) {
 		IGT_TIMEOUT(end_time);
@@ -752,9 +714,6 @@ static int live_empty_request(void *arg)
 out_batch:
 	i915_vma_unpin(batch);
 	i915_vma_put(batch);
-out_unlock:
-	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
-	mutex_unlock(&i915->drm.struct_mutex);
 	return err;
 }
 
@@ -834,7 +793,6 @@ static int live_all_engines(void *arg)
 	struct drm_i915_private *i915 = arg;
 	struct intel_engine_cs *engine;
 	struct i915_request *request[I915_NUM_ENGINES];
-	intel_wakeref_t wakeref;
 	struct igt_live_test t;
 	struct i915_vma *batch;
 	unsigned int id;
@@ -845,18 +803,15 @@ static int live_all_engines(void *arg)
 	 * block doing so, and that they don't complete too soon.
 	 */
 
-	mutex_lock(&i915->drm.struct_mutex);
-	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
-
 	err = igt_live_test_begin(&t, i915, __func__, "");
 	if (err)
-		goto out_unlock;
+		return err;
 
 	batch = recursive_batch(i915);
 	if (IS_ERR(batch)) {
 		err = PTR_ERR(batch);
 		pr_err("%s: Unable to create batch, err=%d\n", __func__, err);
-		goto out_unlock;
+		return err;
 	}
 
 	for_each_engine(engine, i915, id) {
@@ -926,9 +881,6 @@ static int live_all_engines(void *arg)
 			i915_request_put(request[id]);
 	i915_vma_unpin(batch);
 	i915_vma_put(batch);
-out_unlock:
-	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
-	mutex_unlock(&i915->drm.struct_mutex);
 	return err;
 }
 
@@ -938,7 +890,6 @@ static int live_sequential_engines(void *arg)
 	struct i915_request *request[I915_NUM_ENGINES] = {};
 	struct i915_request *prev = NULL;
 	struct intel_engine_cs *engine;
-	intel_wakeref_t wakeref;
 	struct igt_live_test t;
 	unsigned int id;
 	int err;
@@ -949,12 +900,9 @@ static int live_sequential_engines(void *arg)
 	 * they are running on independent engines.
 	 */
 
-	mutex_lock(&i915->drm.struct_mutex);
-	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
-
 	err = igt_live_test_begin(&t, i915, __func__, "");
 	if (err)
-		goto out_unlock;
+		return err;
 
 	for_each_engine(engine, i915, id) {
 		struct i915_vma *batch;
@@ -964,7 +912,7 @@ static int live_sequential_engines(void *arg)
 			err = PTR_ERR(batch);
 			pr_err("%s: Unable to create batch for %s, err=%d\n",
 			       __func__, engine->name, err);
-			goto out_unlock;
+			return err;
 		}
 
 		request[id] = i915_request_create(engine->kernel_context);
@@ -1056,9 +1004,6 @@ static int live_sequential_engines(void *arg)
 		i915_vma_put(request[id]->batch);
 		i915_request_put(request[id]);
 	}
-out_unlock:
-	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
-	mutex_unlock(&i915->drm.struct_mutex);
 	return err;
 }
 
@@ -1149,9 +1094,10 @@ static int live_breadcrumbs_smoketest(void *arg)
 		goto out_threads;
 	}
 
-	mutex_lock(&i915->drm.struct_mutex);
 	for (n = 0; n < t[0].ncontexts; n++) {
+		mutex_lock(&i915->drm.struct_mutex);
 		t[0].contexts[n] = live_context(i915, file);
+		mutex_unlock(&i915->drm.struct_mutex);
 		if (!t[0].contexts[n]) {
 			ret = -ENOMEM;
 			goto out_contexts;
@@ -1168,7 +1114,6 @@ static int live_breadcrumbs_smoketest(void *arg)
 		t[id].max_batch = max_batches(t[0].contexts[0], engine);
 		if (t[id].max_batch < 0) {
 			ret = t[id].max_batch;
-			mutex_unlock(&i915->drm.struct_mutex);
 			goto out_flush;
 		}
 		/* One ring interleaved between requests from all cpus */
@@ -1183,7 +1128,6 @@ static int live_breadcrumbs_smoketest(void *arg)
 					  &t[id], "igt/%d.%d", id, n);
 			if (IS_ERR(tsk)) {
 				ret = PTR_ERR(tsk);
-				mutex_unlock(&i915->drm.struct_mutex);
 				goto out_flush;
 			}
 
@@ -1191,7 +1135,6 @@ static int live_breadcrumbs_smoketest(void *arg)
 			threads[id * ncpus + n] = tsk;
 		}
 	}
-	mutex_unlock(&i915->drm.struct_mutex);
 
 	msleep(jiffies_to_msecs(i915_selftest.timeout_jiffies));
 
@@ -1219,10 +1162,8 @@ static int live_breadcrumbs_smoketest(void *arg)
 	pr_info("Completed %lu waits for %lu fences across %d engines and %d cpus\n",
 		num_waits, num_fences, RUNTIME_INFO(i915)->num_engines, ncpus);
 
-	mutex_lock(&i915->drm.struct_mutex);
 	ret = igt_live_test_end(&live) ?: ret;
 out_contexts:
-	mutex_unlock(&i915->drm.struct_mutex);
 	kfree(t[0].contexts);
 out_threads:
 	kfree(threads);
diff --git a/drivers/gpu/drm/i915/selftests/i915_selftest.c b/drivers/gpu/drm/i915/selftests/i915_selftest.c
index 438ea0eaa416..825a8286cbe8 100644
--- a/drivers/gpu/drm/i915/selftests/i915_selftest.c
+++ b/drivers/gpu/drm/i915/selftests/i915_selftest.c
@@ -263,10 +263,8 @@ int __i915_live_teardown(int err, void *data)
 {
 	struct drm_i915_private *i915 = data;
 
-	mutex_lock(&i915->drm.struct_mutex);
-	if (igt_flush_test(i915, I915_WAIT_LOCKED))
+	if (igt_flush_test(i915))
 		err = -EIO;
-	mutex_unlock(&i915->drm.struct_mutex);
 
 	i915_gem_drain_freed_objects(i915);
 
@@ -284,10 +282,8 @@ int __intel_gt_live_teardown(int err, void *data)
 {
 	struct intel_gt *gt = data;
 
-	mutex_lock(&gt->i915->drm.struct_mutex);
-	if (igt_flush_test(gt->i915, I915_WAIT_LOCKED))
+	if (igt_flush_test(gt->i915))
 		err = -EIO;
-	mutex_unlock(&gt->i915->drm.struct_mutex);
 
 	i915_gem_drain_freed_objects(gt->i915);
 
diff --git a/drivers/gpu/drm/i915/selftests/i915_vma.c b/drivers/gpu/drm/i915/selftests/i915_vma.c
index e0ca12c17a7f..6c1705058b93 100644
--- a/drivers/gpu/drm/i915/selftests/i915_vma.c
+++ b/drivers/gpu/drm/i915/selftests/i915_vma.c
@@ -835,12 +835,8 @@ int i915_vma_mock_selftests(void)
 
 	err = i915_subtests(tests, ggtt);
 
-	mutex_lock(&i915->drm.struct_mutex);
 	mock_device_flush(i915);
-	mutex_unlock(&i915->drm.struct_mutex);
-
 	i915_gem_drain_freed_objects(i915);
-
 	mock_fini_ggtt(ggtt);
 	kfree(ggtt);
 out_put:
diff --git a/drivers/gpu/drm/i915/selftests/igt_flush_test.c b/drivers/gpu/drm/i915/selftests/igt_flush_test.c
index d3b5eb402d33..2a5fbe46ea9f 100644
--- a/drivers/gpu/drm/i915/selftests/igt_flush_test.c
+++ b/drivers/gpu/drm/i915/selftests/igt_flush_test.c
@@ -12,31 +12,25 @@
 
 #include "igt_flush_test.h"
 
-int igt_flush_test(struct drm_i915_private *i915, unsigned int flags)
+int igt_flush_test(struct drm_i915_private *i915)
 {
 	int ret = intel_gt_is_wedged(&i915->gt) ? -EIO : 0;
-	int repeat = !!(flags & I915_WAIT_LOCKED);
 
 	cond_resched();
 
-	do {
-		if (i915_gem_wait_for_idle(i915, flags, HZ / 5) == -ETIME) {
-			pr_err("%pS timed out, cancelling all further testing.\n",
-			       __builtin_return_address(0));
+	i915_retire_requests(i915);
+	if (i915_gem_wait_for_idle(i915, 0, HZ / 5) == -ETIME) {
+		pr_err("%pS timed out, cancelling all further testing.\n",
+		       __builtin_return_address(0));
 
-			GEM_TRACE("%pS timed out.\n",
-				  __builtin_return_address(0));
-			GEM_TRACE_DUMP();
+		GEM_TRACE("%pS timed out.\n",
+			  __builtin_return_address(0));
+		GEM_TRACE_DUMP();
 
-			intel_gt_set_wedged(&i915->gt);
-			repeat = 0;
-			ret = -EIO;
-		}
-
-		/* Ensure we also flush after wedging. */
-		if (flags & I915_WAIT_LOCKED)
-			i915_retire_requests(i915);
-	} while (repeat--);
+		intel_gt_set_wedged(&i915->gt);
+		ret = -EIO;
+	}
+	i915_retire_requests(i915);
 
 	return ret;
 }
diff --git a/drivers/gpu/drm/i915/selftests/igt_flush_test.h b/drivers/gpu/drm/i915/selftests/igt_flush_test.h
index 63e009927c43..7541fa74e641 100644
--- a/drivers/gpu/drm/i915/selftests/igt_flush_test.h
+++ b/drivers/gpu/drm/i915/selftests/igt_flush_test.h
@@ -9,6 +9,6 @@
 
 struct drm_i915_private;
 
-int igt_flush_test(struct drm_i915_private *i915, unsigned int flags);
+int igt_flush_test(struct drm_i915_private *i915);
 
 #endif /* IGT_FLUSH_TEST_H */
diff --git a/drivers/gpu/drm/i915/selftests/igt_live_test.c b/drivers/gpu/drm/i915/selftests/igt_live_test.c
index 3e902761cd16..04a6f88fdf64 100644
--- a/drivers/gpu/drm/i915/selftests/igt_live_test.c
+++ b/drivers/gpu/drm/i915/selftests/igt_live_test.c
@@ -19,15 +19,12 @@ int igt_live_test_begin(struct igt_live_test *t,
 	enum intel_engine_id id;
 	int err;
 
-	lockdep_assert_held(&i915->drm.struct_mutex);
-
 	t->i915 = i915;
 	t->func = func;
 	t->name = name;
 
 	err = i915_gem_wait_for_idle(i915,
-				     I915_WAIT_INTERRUPTIBLE |
-				     I915_WAIT_LOCKED,
+				     I915_WAIT_INTERRUPTIBLE,
 				     MAX_SCHEDULE_TIMEOUT);
 	if (err) {
 		pr_err("%s(%s): failed to idle before, with err=%d!",
@@ -50,9 +47,7 @@ int igt_live_test_end(struct igt_live_test *t)
 	struct intel_engine_cs *engine;
 	enum intel_engine_id id;
 
-	lockdep_assert_held(&i915->drm.struct_mutex);
-
-	if (igt_flush_test(i915, I915_WAIT_LOCKED))
+	if (igt_flush_test(i915))
 		return -EIO;
 
 	if (t->reset_global != i915_reset_count(&i915->gpu_error)) {
diff --git a/drivers/gpu/drm/i915/selftests/mock_gem_device.c b/drivers/gpu/drm/i915/selftests/mock_gem_device.c
index 01a89c071bf5..1956006a0d5b 100644
--- a/drivers/gpu/drm/i915/selftests/mock_gem_device.c
+++ b/drivers/gpu/drm/i915/selftests/mock_gem_device.c
@@ -41,8 +41,6 @@ void mock_device_flush(struct drm_i915_private *i915)
 	struct intel_engine_cs *engine;
 	enum intel_engine_id id;
 
-	lockdep_assert_held(&i915->drm.struct_mutex);
-
 	do {
 		for_each_engine(engine, i915, id)
 			mock_engine_flush(engine);
@@ -55,9 +53,7 @@ static void mock_device_release(struct drm_device *dev)
 	struct intel_engine_cs *engine;
 	enum intel_engine_id id;
 
-	mutex_lock(&i915->drm.struct_mutex);
 	mock_device_flush(i915);
-	mutex_unlock(&i915->drm.struct_mutex);
 
 	flush_work(&i915->gem.idle_work);
 	i915_gem_drain_workqueue(i915);
-- 
2.23.0

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

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

* [PATCH 18/21] drm/i915: Remove the GEM idle worker
  2019-09-02  4:02 [PATCH 01/21] drm/i915: Restrict the aliasing-ppgtt to the size of the ggtt Chris Wilson
                   ` (15 preceding siblings ...)
  2019-09-02  4:02 ` [PATCH 17/21] drm/i915: Drop struct_mutex from around i915_retire_requests() Chris Wilson
@ 2019-09-02  4:03 ` Chris Wilson
  2019-09-24 15:26   ` Tvrtko Ursulin
  2019-09-02  4:03 ` [PATCH 19/21] drm/i915: Merge wait_for_timelines with retire_request Chris Wilson
                   ` (6 subsequent siblings)
  23 siblings, 1 reply; 53+ messages in thread
From: Chris Wilson @ 2019-09-02  4:03 UTC (permalink / raw)
  To: intel-gfx

Nothing inside the idle worker now requires struct_mutex, so we can
remove the indirection of using our own worker.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 drivers/gpu/drm/i915/gem/i915_gem_pm.c        | 28 ++-----------------
 .../drm/i915/gem/selftests/i915_gem_mman.c    |  3 --
 drivers/gpu/drm/i915/i915_debugfs.c           |  5 ----
 drivers/gpu/drm/i915/i915_drv.h               |  9 ------
 .../gpu/drm/i915/selftests/mock_gem_device.c  |  6 ----
 5 files changed, 2 insertions(+), 49 deletions(-)

diff --git a/drivers/gpu/drm/i915/gem/i915_gem_pm.c b/drivers/gpu/drm/i915/gem/i915_gem_pm.c
index fec0b410d1d9..e83eed8fa452 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_pm.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_pm.c
@@ -13,36 +13,13 @@
 
 static void i915_gem_park(struct drm_i915_private *i915)
 {
-	lockdep_assert_held(&i915->drm.struct_mutex);
+	cancel_delayed_work(&i915->gem.retire_work);
 
 	i915_vma_parked(i915);
 
 	i915_globals_park();
 }
 
-static void idle_work_handler(struct work_struct *work)
-{
-	struct drm_i915_private *i915 =
-		container_of(work, typeof(*i915), gem.idle_work);
-	bool park;
-
-	cancel_delayed_work_sync(&i915->gem.retire_work);
-	mutex_lock(&i915->drm.struct_mutex);
-
-	intel_wakeref_lock(&i915->gt.wakeref);
-	park = (!intel_wakeref_is_active(&i915->gt.wakeref) &&
-		!work_pending(work));
-	intel_wakeref_unlock(&i915->gt.wakeref);
-	if (park)
-		i915_gem_park(i915);
-	else
-		queue_delayed_work(i915->wq,
-				   &i915->gem.retire_work,
-				   round_jiffies_up_relative(HZ));
-
-	mutex_unlock(&i915->drm.struct_mutex);
-}
-
 static void retire_work_handler(struct work_struct *work)
 {
 	struct drm_i915_private *i915 =
@@ -71,7 +48,7 @@ static int pm_notifier(struct notifier_block *nb,
 		break;
 
 	case INTEL_GT_PARK:
-		queue_work(i915->wq, &i915->gem.idle_work);
+		i915_gem_park(i915);
 		break;
 	}
 
@@ -244,7 +221,6 @@ void i915_gem_resume(struct drm_i915_private *i915)
 
 void i915_gem_init__pm(struct drm_i915_private *i915)
 {
-	INIT_WORK(&i915->gem.idle_work, idle_work_handler);
 	INIT_DELAYED_WORK(&i915->gem.retire_work, retire_work_handler);
 
 	i915->gem.pm_notifier.notifier_call = pm_notifier;
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 39c01bc4eb51..8563af1819c4 100644
--- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c
+++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c
@@ -384,11 +384,8 @@ static bool assert_mmap_offset(struct drm_i915_private *i915,
 static void disable_retire_worker(struct drm_i915_private *i915)
 {
 	i915_gem_driver_unregister__shrinker(i915);
-
 	intel_gt_pm_get(&i915->gt);
-
 	cancel_delayed_work_sync(&i915->gem.retire_work);
-	flush_work(&i915->gem.idle_work);
 }
 
 static void restore_retire_worker(struct drm_i915_private *i915)
diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
index 55f0fc03aa3e..09c6c485a732 100644
--- a/drivers/gpu/drm/i915/i915_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
@@ -3642,11 +3642,6 @@ i915_drop_caches_set(void *data, u64 val)
 		i915_gem_shrink_all(i915);
 	fs_reclaim_release(GFP_KERNEL);
 
-	if (val & DROP_IDLE) {
-		flush_delayed_work(&i915->gem.retire_work);
-		flush_work(&i915->gem.idle_work);
-	}
-
 	if (val & DROP_FREED)
 		i915_gem_drain_freed_objects(i915);
 
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index db7480831e52..b33fc7972e6b 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -1718,15 +1718,6 @@ struct drm_i915_private {
 		 * fires, go retire requests.
 		 */
 		struct delayed_work retire_work;
-
-		/**
-		 * When we detect an idle GPU, we want to turn on
-		 * powersaving features. So once we see that there
-		 * are no more requests outstanding and no more
-		 * arrive within a small period of time, we fire
-		 * off the idle_work.
-		 */
-		struct work_struct idle_work;
 	} gem;
 
 	/* For i945gm vblank irq vs. C3 workaround */
diff --git a/drivers/gpu/drm/i915/selftests/mock_gem_device.c b/drivers/gpu/drm/i915/selftests/mock_gem_device.c
index 1956006a0d5b..f3e9b5d7d098 100644
--- a/drivers/gpu/drm/i915/selftests/mock_gem_device.c
+++ b/drivers/gpu/drm/i915/selftests/mock_gem_device.c
@@ -55,7 +55,6 @@ static void mock_device_release(struct drm_device *dev)
 
 	mock_device_flush(i915);
 
-	flush_work(&i915->gem.idle_work);
 	i915_gem_drain_workqueue(i915);
 
 	mutex_lock(&i915->drm.struct_mutex);
@@ -103,10 +102,6 @@ static void mock_retire_work_handler(struct work_struct *work)
 {
 }
 
-static void mock_idle_work_handler(struct work_struct *work)
-{
-}
-
 static int pm_domain_resume(struct device *dev)
 {
 	return pm_generic_runtime_resume(dev);
@@ -186,7 +181,6 @@ struct drm_i915_private *mock_gem_device(void)
 	mock_init_contexts(i915);
 
 	INIT_DELAYED_WORK(&i915->gem.retire_work, mock_retire_work_handler);
-	INIT_WORK(&i915->gem.idle_work, mock_idle_work_handler);
 
 	i915->gt.awake = true;
 
-- 
2.23.0

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

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

* [PATCH 19/21] drm/i915: Merge wait_for_timelines with retire_request
  2019-09-02  4:02 [PATCH 01/21] drm/i915: Restrict the aliasing-ppgtt to the size of the ggtt Chris Wilson
                   ` (16 preceding siblings ...)
  2019-09-02  4:03 ` [PATCH 18/21] drm/i915: Remove the GEM idle worker Chris Wilson
@ 2019-09-02  4:03 ` Chris Wilson
  2019-09-24 15:57   ` Tvrtko Ursulin
  2019-09-02  4:03 ` [PATCH 20/21] drm/i915: Move request runtime management onto gt Chris Wilson
                   ` (5 subsequent siblings)
  23 siblings, 1 reply; 53+ messages in thread
From: Chris Wilson @ 2019-09-02  4:03 UTC (permalink / raw)
  To: intel-gfx

wait_for_timelines is essentially the same loop as retiring requests
(with an extra), so merge the two into one routine.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 drivers/gpu/drm/i915/gem/i915_gem_mman.c      |  4 +-
 drivers/gpu/drm/i915/gem/i915_gem_pm.c        |  6 +-
 .../drm/i915/gem/selftests/i915_gem_context.c |  4 +-
 drivers/gpu/drm/i915/gt/selftest_timeline.c   |  2 +-
 drivers/gpu/drm/i915/i915_debugfs.c           |  6 +-
 drivers/gpu/drm/i915/i915_drv.h               |  3 +-
 drivers/gpu/drm/i915/i915_gem.c               | 68 ++-----------------
 drivers/gpu/drm/i915/i915_gem_evict.c         | 12 ++--
 drivers/gpu/drm/i915/i915_gem_gtt.c           |  2 +-
 drivers/gpu/drm/i915/i915_request.c           | 21 +++++-
 drivers/gpu/drm/i915/i915_request.h           |  3 +-
 .../gpu/drm/i915/selftests/igt_flush_test.c   |  4 +-
 .../gpu/drm/i915/selftests/igt_live_test.c    |  4 +-
 .../gpu/drm/i915/selftests/mock_gem_device.c  |  2 +-
 14 files changed, 42 insertions(+), 99 deletions(-)

diff --git a/drivers/gpu/drm/i915/gem/i915_gem_mman.c b/drivers/gpu/drm/i915/gem/i915_gem_mman.c
index 9a8c307c5aeb..761ab0076a6a 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_mman.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_mman.c
@@ -429,9 +429,7 @@ static int create_mmap_offset(struct drm_i915_gem_object *obj)
 
 	/* Attempt to reap some mmap space from dead objects */
 	do {
-		err = i915_gem_wait_for_idle(i915,
-					     I915_WAIT_INTERRUPTIBLE,
-					     MAX_SCHEDULE_TIMEOUT);
+		err = i915_gem_wait_for_idle(i915, MAX_SCHEDULE_TIMEOUT);
 		if (err)
 			break;
 
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_pm.c b/drivers/gpu/drm/i915/gem/i915_gem_pm.c
index e83eed8fa452..afbcf9219267 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_pm.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_pm.c
@@ -25,7 +25,7 @@ static void retire_work_handler(struct work_struct *work)
 	struct drm_i915_private *i915 =
 		container_of(work, typeof(*i915), gem.retire_work.work);
 
-	i915_retire_requests(i915);
+	i915_retire_requests(i915, 0);
 
 	queue_delayed_work(i915->wq,
 			   &i915->gem.retire_work,
@@ -59,9 +59,7 @@ static bool switch_to_kernel_context_sync(struct intel_gt *gt)
 {
 	bool result = !intel_gt_is_wedged(gt);
 
-	if (i915_gem_wait_for_idle(gt->i915,
-				   I915_WAIT_FOR_IDLE_BOOST,
-				   I915_GEM_IDLE_TIMEOUT) == -ETIME) {
+	if (i915_gem_wait_for_idle(gt->i915, I915_GEM_IDLE_TIMEOUT) == -ETIME) {
 		/* XXX hide warning from gem_eio */
 		if (i915_modparams.reset) {
 			dev_err(gt->i915->drm.dev,
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 b87e35a713b8..bc4c8d763024 100644
--- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c
+++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c
@@ -304,7 +304,7 @@ create_test_object(struct i915_address_space *vm,
 	int err;
 
 	/* Keep in GEM's good graces */
-	i915_retire_requests(vm->i915);
+	i915_retire_requests(vm->i915, 0);
 
 	size = min(vm->total / 2, 1024ull * DW_PER_PAGE * PAGE_SIZE);
 	size = round_down(size, DW_PER_PAGE * PAGE_SIZE);
@@ -923,7 +923,7 @@ __sseu_finish(const char *name,
 
 	if ((flags & TEST_IDLE) && ret == 0) {
 		ret = i915_gem_wait_for_idle(ce->engine->i915,
-					     0, MAX_SCHEDULE_TIMEOUT);
+					     MAX_SCHEDULE_TIMEOUT);
 		if (ret)
 			return ret;
 
diff --git a/drivers/gpu/drm/i915/gt/selftest_timeline.c b/drivers/gpu/drm/i915/gt/selftest_timeline.c
index 16abfabf08c7..b0b0fa5f91de 100644
--- a/drivers/gpu/drm/i915/gt/selftest_timeline.c
+++ b/drivers/gpu/drm/i915/gt/selftest_timeline.c
@@ -734,7 +734,7 @@ static int live_hwsp_wrap(void *arg)
 			goto out;
 		}
 
-		i915_retire_requests(i915); /* recycle HWSP */
+		i915_retire_requests(i915, 0); /* recycle HWSP */
 	}
 
 out:
diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
index 09c6c485a732..d7410f3f576f 100644
--- a/drivers/gpu/drm/i915/i915_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
@@ -3612,12 +3612,10 @@ i915_drop_caches_set(void *data, u64 val)
 		intel_gt_set_wedged(&i915->gt);
 
 	if (val & DROP_RETIRE)
-		i915_retire_requests(i915);
+		i915_retire_requests(i915, 0);
 
 	if (val & (DROP_IDLE | DROP_ACTIVE)) {
-		ret = i915_gem_wait_for_idle(i915,
-					     I915_WAIT_INTERRUPTIBLE,
-					     MAX_SCHEDULE_TIMEOUT);
+		ret = i915_gem_wait_for_idle(i915, MAX_SCHEDULE_TIMEOUT);
 		if (ret)
 			return ret;
 	}
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index b33fc7972e6b..3d1d652431be 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -2314,8 +2314,7 @@ void i915_gem_driver_register(struct drm_i915_private *i915);
 void i915_gem_driver_unregister(struct drm_i915_private *i915);
 void i915_gem_driver_remove(struct drm_i915_private *dev_priv);
 void i915_gem_driver_release(struct drm_i915_private *dev_priv);
-int i915_gem_wait_for_idle(struct drm_i915_private *dev_priv,
-			   unsigned int flags, long timeout);
+int i915_gem_wait_for_idle(struct drm_i915_private *dev_priv, long timeout);
 void i915_gem_suspend(struct drm_i915_private *dev_priv);
 void i915_gem_suspend_late(struct drm_i915_private *dev_priv);
 void i915_gem_resume(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 225fd22af858..c5f1c2043f97 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -890,79 +890,19 @@ void i915_gem_runtime_suspend(struct drm_i915_private *i915)
 	}
 }
 
-static long
-wait_for_timelines(struct drm_i915_private *i915,
-		   unsigned int wait, long timeout)
-{
-	struct intel_gt_timelines *timelines = &i915->gt.timelines;
-	struct intel_timeline *tl;
-	unsigned long flags;
-
-	spin_lock_irqsave(&timelines->lock, flags);
-	list_for_each_entry(tl, &timelines->active_list, link) {
-		struct dma_fence *fence;
-
-		fence = i915_active_fence_get(&tl->last_request);
-		if (!fence)
-			continue;
-
-		spin_unlock_irqrestore(&timelines->lock, flags);
-
-		if (!dma_fence_is_i915(fence)) {
-			timeout = dma_fence_wait_timeout(fence,
-							 flags & I915_WAIT_INTERRUPTIBLE,
-							 timeout);
-		} else {
-			struct i915_request *rq = to_request(fence);
-
-			/*
-			 * "Race-to-idle".
-			 *
-			 * Switching to the kernel context is often used as
-			 * a synchronous step prior to idling, e.g. in suspend
-			 * for flushing all current operations to memory before
-			 * sleeping. These we want to complete as quickly as
-			 * possible to avoid prolonged stalls, so allow the gpu
-			 * to boost to maximum clocks.
-			 */
-			if (flags & I915_WAIT_FOR_IDLE_BOOST)
-				gen6_rps_boost(rq);
-
-			timeout = i915_request_wait(rq, flags, timeout);
-		}
-
-		dma_fence_put(fence);
-		if (timeout < 0)
-			return timeout;
-
-		/* restart after reacquiring the lock */
-		spin_lock_irqsave(&timelines->lock, flags);
-		tl = list_entry(&timelines->active_list, typeof(*tl), link);
-	}
-	spin_unlock_irqrestore(&timelines->lock, flags);
-
-	return timeout;
-}
-
-int i915_gem_wait_for_idle(struct drm_i915_private *i915,
-			   unsigned int flags, long timeout)
+int i915_gem_wait_for_idle(struct drm_i915_private *i915, long timeout)
 {
 	/* If the device is asleep, we have no requests outstanding */
 	if (!intel_gt_pm_is_awake(&i915->gt))
 		return 0;
 
-	do {
-		timeout = wait_for_timelines(i915, flags, timeout);
-		if (timeout < 0)
-			return timeout;
-
+	while ((timeout = i915_retire_requests(i915, timeout)) > 0) {
 		cond_resched();
 		if (signal_pending(current))
 			return -EINTR;
+	}
 
-	} while (i915_retire_requests(i915));
-
-	return 0;
+	return timeout;
 }
 
 struct i915_vma *
diff --git a/drivers/gpu/drm/i915/i915_gem_evict.c b/drivers/gpu/drm/i915/i915_gem_evict.c
index 44f5b638fa43..708055a3887e 100644
--- a/drivers/gpu/drm/i915/i915_gem_evict.c
+++ b/drivers/gpu/drm/i915/i915_gem_evict.c
@@ -46,9 +46,7 @@ static int ggtt_flush(struct drm_i915_private *i915)
 	 * the hopes that we can then remove contexts and the like only
 	 * bound by their active reference.
 	 */
-	return i915_gem_wait_for_idle(i915,
-				      I915_WAIT_INTERRUPTIBLE,
-				      MAX_SCHEDULE_TIMEOUT);
+	return i915_gem_wait_for_idle(i915, MAX_SCHEDULE_TIMEOUT);
 }
 
 static bool
@@ -126,6 +124,8 @@ i915_gem_evict_something(struct i915_address_space *vm,
 				    min_size, alignment, cache_level,
 				    start, end, mode);
 
+	i915_retire_requests(vm->i915, 0);
+
 search_again:
 	active = NULL;
 	INIT_LIST_HEAD(&eviction_list);
@@ -265,13 +265,13 @@ int i915_gem_evict_for_node(struct i915_address_space *vm,
 
 	trace_i915_gem_evict_node(vm, target, flags);
 
-	/* Retire before we search the active list. Although we have
+	/*
+	 * Retire before we search the active list. Although we have
 	 * reasonable accuracy in our retirement lists, we may have
 	 * a stray pin (preventing eviction) that can only be resolved by
 	 * retiring.
 	 */
-	if (!(flags & PIN_NONBLOCK))
-		i915_retire_requests(vm->i915);
+	i915_retire_requests(vm->i915, 0);
 
 	check_color = vm->mm.color_adjust;
 	if (check_color) {
diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
index 60676de059a7..2b7a4d49b2e6 100644
--- a/drivers/gpu/drm/i915/i915_gem_gtt.c
+++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
@@ -2524,7 +2524,7 @@ void i915_gem_gtt_finish_pages(struct drm_i915_gem_object *obj,
 	struct i915_ggtt *ggtt = &dev_priv->ggtt;
 
 	if (unlikely(ggtt->do_idle_maps)) {
-		if (i915_gem_wait_for_idle(dev_priv, 0, MAX_SCHEDULE_TIMEOUT)) {
+		if (i915_retire_requests(dev_priv, MAX_SCHEDULE_TIMEOUT)) {
 			DRM_ERROR("Failed to wait for idle; VT'd may hang.\n");
 			/* Wait a bit, in hopes it avoids the hang */
 			udelay(10);
diff --git a/drivers/gpu/drm/i915/i915_request.c b/drivers/gpu/drm/i915/i915_request.c
index 4ecfae143276..1c5e804c9ca2 100644
--- a/drivers/gpu/drm/i915/i915_request.c
+++ b/drivers/gpu/drm/i915/i915_request.c
@@ -1429,10 +1429,11 @@ long i915_request_wait(struct i915_request *rq,
 	return timeout;
 }
 
-bool i915_retire_requests(struct drm_i915_private *i915)
+long i915_retire_requests(struct drm_i915_private *i915, long timeout)
 {
 	struct intel_gt_timelines *timelines = &i915->gt.timelines;
 	struct intel_timeline *tl, *tn;
+	unsigned long active_count = 0;
 	unsigned long flags;
 	LIST_HEAD(free);
 
@@ -1446,13 +1447,27 @@ bool i915_retire_requests(struct drm_i915_private *i915)
 		tl->active_count++; /* pin the list element */
 		spin_unlock_irqrestore(&timelines->lock, flags);
 
+		if (timeout > 0) {
+			struct dma_fence *fence;
+
+			fence = i915_active_fence_get(&tl->last_request);
+			if (fence) {
+				timeout = dma_fence_wait_timeout(fence,
+								 true,
+								 timeout);
+				dma_fence_put(fence);
+			}
+		}
+
 		retire_requests(tl);
 
 		spin_lock_irqsave(&timelines->lock, flags);
 
 		/* Resume iteration after dropping lock */
 		list_safe_reset_next(tl, tn, link);
-		if (!--tl->active_count)
+		if (--tl->active_count)
+			active_count += !!rcu_access_pointer(tl->last_request.fence);
+		else
 			list_del(&tl->link);
 
 		mutex_unlock(&tl->mutex);
@@ -1468,7 +1483,7 @@ bool i915_retire_requests(struct drm_i915_private *i915)
 	list_for_each_entry_safe(tl, tn, &free, link)
 		__intel_timeline_free(&tl->kref);
 
-	return !list_empty(&timelines->active_list);
+	return active_count ? timeout : 0;
 }
 
 #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
diff --git a/drivers/gpu/drm/i915/i915_request.h b/drivers/gpu/drm/i915/i915_request.h
index 57a2193c64d1..2a5d682aa6b1 100644
--- a/drivers/gpu/drm/i915/i915_request.h
+++ b/drivers/gpu/drm/i915/i915_request.h
@@ -310,7 +310,6 @@ long i915_request_wait(struct i915_request *rq,
 #define I915_WAIT_INTERRUPTIBLE	BIT(0)
 #define I915_WAIT_PRIORITY	BIT(1) /* small priority bump for the request */
 #define I915_WAIT_ALL		BIT(2) /* used by i915_gem_object_wait() */
-#define I915_WAIT_FOR_IDLE_BOOST BIT(3)
 
 static inline bool i915_request_signaled(const struct i915_request *rq)
 {
@@ -440,6 +439,6 @@ static inline bool i915_request_has_nopreempt(const struct i915_request *rq)
 	return unlikely(rq->flags & I915_REQUEST_NOPREEMPT);
 }
 
-bool i915_retire_requests(struct drm_i915_private *i915);
+long i915_retire_requests(struct drm_i915_private *i915, long timeout);
 
 #endif /* I915_REQUEST_H */
diff --git a/drivers/gpu/drm/i915/selftests/igt_flush_test.c b/drivers/gpu/drm/i915/selftests/igt_flush_test.c
index 2a5fbe46ea9f..ed496bd6d84f 100644
--- a/drivers/gpu/drm/i915/selftests/igt_flush_test.c
+++ b/drivers/gpu/drm/i915/selftests/igt_flush_test.c
@@ -18,8 +18,7 @@ int igt_flush_test(struct drm_i915_private *i915)
 
 	cond_resched();
 
-	i915_retire_requests(i915);
-	if (i915_gem_wait_for_idle(i915, 0, HZ / 5) == -ETIME) {
+	if (i915_gem_wait_for_idle(i915, HZ / 5) == -ETIME) {
 		pr_err("%pS timed out, cancelling all further testing.\n",
 		       __builtin_return_address(0));
 
@@ -30,7 +29,6 @@ int igt_flush_test(struct drm_i915_private *i915)
 		intel_gt_set_wedged(&i915->gt);
 		ret = -EIO;
 	}
-	i915_retire_requests(i915);
 
 	return ret;
 }
diff --git a/drivers/gpu/drm/i915/selftests/igt_live_test.c b/drivers/gpu/drm/i915/selftests/igt_live_test.c
index 04a6f88fdf64..eae90f97df6c 100644
--- a/drivers/gpu/drm/i915/selftests/igt_live_test.c
+++ b/drivers/gpu/drm/i915/selftests/igt_live_test.c
@@ -23,9 +23,7 @@ int igt_live_test_begin(struct igt_live_test *t,
 	t->func = func;
 	t->name = name;
 
-	err = i915_gem_wait_for_idle(i915,
-				     I915_WAIT_INTERRUPTIBLE,
-				     MAX_SCHEDULE_TIMEOUT);
+	err = i915_gem_wait_for_idle(i915, MAX_SCHEDULE_TIMEOUT);
 	if (err) {
 		pr_err("%s(%s): failed to idle before, with err=%d!",
 		       func, name, err);
diff --git a/drivers/gpu/drm/i915/selftests/mock_gem_device.c b/drivers/gpu/drm/i915/selftests/mock_gem_device.c
index f3e9b5d7d098..66cc5634db1c 100644
--- a/drivers/gpu/drm/i915/selftests/mock_gem_device.c
+++ b/drivers/gpu/drm/i915/selftests/mock_gem_device.c
@@ -44,7 +44,7 @@ void mock_device_flush(struct drm_i915_private *i915)
 	do {
 		for_each_engine(engine, i915, id)
 			mock_engine_flush(engine);
-	} while (i915_retire_requests(i915));
+	} while (i915_retire_requests(i915, MAX_SCHEDULE_TIMEOUT));
 }
 
 static void mock_device_release(struct drm_device *dev)
-- 
2.23.0

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

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

* [PATCH 20/21] drm/i915: Move request runtime management onto gt
  2019-09-02  4:02 [PATCH 01/21] drm/i915: Restrict the aliasing-ppgtt to the size of the ggtt Chris Wilson
                   ` (17 preceding siblings ...)
  2019-09-02  4:03 ` [PATCH 19/21] drm/i915: Merge wait_for_timelines with retire_request Chris Wilson
@ 2019-09-02  4:03 ` Chris Wilson
  2019-09-02  4:03 ` [PATCH 21/21] drm/i915: Move global activity tracking from GEM to GT Chris Wilson
                   ` (4 subsequent siblings)
  23 siblings, 0 replies; 53+ messages in thread
From: Chris Wilson @ 2019-09-02  4:03 UTC (permalink / raw)
  To: intel-gfx

Requests are run from the gt and are tided into the gt runtime power
management, so pull the runtime request management under gt/

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 drivers/gpu/drm/i915/Makefile                 |   1 +
 drivers/gpu/drm/i915/gem/i915_gem_mman.c      |   6 +-
 drivers/gpu/drm/i915/gem/i915_gem_pm.c        |  22 +---
 .../drm/i915/gem/selftests/i915_gem_context.c |   5 +-
 .../drm/i915/gem/selftests/i915_gem_mman.c    |   2 +-
 drivers/gpu/drm/i915/gt/intel_gt.c            |   2 +
 drivers/gpu/drm/i915/gt/intel_gt_pm.c         |   9 +-
 drivers/gpu/drm/i915/gt/intel_gt_requests.c   | 118 ++++++++++++++++++
 drivers/gpu/drm/i915/gt/intel_gt_requests.h   |  19 +++
 drivers/gpu/drm/i915/gt/intel_gt_types.h      |  11 ++
 drivers/gpu/drm/i915/gt/selftest_timeline.c   |   8 +-
 drivers/gpu/drm/i915/i915_debugfs.c           |  17 +--
 drivers/gpu/drm/i915/i915_drv.h               |  10 --
 drivers/gpu/drm/i915/i915_gem.c               |  15 ---
 drivers/gpu/drm/i915/i915_gem_evict.c         |  14 +--
 drivers/gpu/drm/i915/i915_gem_gtt.c           |   4 +-
 drivers/gpu/drm/i915/i915_request.c           |  59 +--------
 drivers/gpu/drm/i915/i915_request.h           |   3 +-
 .../gpu/drm/i915/selftests/igt_flush_test.c   |   9 +-
 .../gpu/drm/i915/selftests/igt_live_test.c    |   5 +-
 .../gpu/drm/i915/selftests/mock_gem_device.c  |   9 +-
 21 files changed, 203 insertions(+), 145 deletions(-)
 create mode 100644 drivers/gpu/drm/i915/gt/intel_gt_requests.c
 create mode 100644 drivers/gpu/drm/i915/gt/intel_gt_requests.h

diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
index 658b930d34a8..ab5fa58d24f7 100644
--- a/drivers/gpu/drm/i915/Makefile
+++ b/drivers/gpu/drm/i915/Makefile
@@ -83,6 +83,7 @@ gt-y += \
 	gt/intel_gt_irq.o \
 	gt/intel_gt_pm.o \
 	gt/intel_gt_pm_irq.o \
+	gt/intel_gt_requests.o \
 	gt/intel_hangcheck.o \
 	gt/intel_lrc.o \
 	gt/intel_renderstate.o \
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_mman.c b/drivers/gpu/drm/i915/gem/i915_gem_mman.c
index 761ab0076a6a..e0bfc021ec6f 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_mman.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_mman.c
@@ -8,6 +8,7 @@
 #include <linux/sizes.h>
 
 #include "gt/intel_gt.h"
+#include "gt/intel_gt_requests.h"
 
 #include "i915_drv.h"
 #include "i915_gem_gtt.h"
@@ -421,6 +422,7 @@ void i915_gem_object_release_mmap(struct drm_i915_gem_object *obj)
 static int create_mmap_offset(struct drm_i915_gem_object *obj)
 {
 	struct drm_i915_private *i915 = to_i915(obj->base.dev);
+	struct intel_gt *gt = &i915->gt;
 	int err;
 
 	err = drm_gem_create_mmap_offset(&obj->base);
@@ -429,7 +431,7 @@ static int create_mmap_offset(struct drm_i915_gem_object *obj)
 
 	/* Attempt to reap some mmap space from dead objects */
 	do {
-		err = i915_gem_wait_for_idle(i915, MAX_SCHEDULE_TIMEOUT);
+		err = intel_gt_wait_for_idle(gt, MAX_SCHEDULE_TIMEOUT);
 		if (err)
 			break;
 
@@ -438,7 +440,7 @@ static int create_mmap_offset(struct drm_i915_gem_object *obj)
 		if (!err)
 			break;
 
-	} while (flush_delayed_work(&i915->gem.retire_work));
+	} while (flush_delayed_work(&gt->requests.retire_work));
 
 	return err;
 }
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_pm.c b/drivers/gpu/drm/i915/gem/i915_gem_pm.c
index afbcf9219267..b459719386e3 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_pm.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_pm.c
@@ -7,31 +7,18 @@
 #include "gem/i915_gem_pm.h"
 #include "gt/intel_gt.h"
 #include "gt/intel_gt_pm.h"
+#include "gt/intel_gt_requests.h"
 
 #include "i915_drv.h"
 #include "i915_globals.h"
 
 static void i915_gem_park(struct drm_i915_private *i915)
 {
-	cancel_delayed_work(&i915->gem.retire_work);
-
 	i915_vma_parked(i915);
 
 	i915_globals_park();
 }
 
-static void retire_work_handler(struct work_struct *work)
-{
-	struct drm_i915_private *i915 =
-		container_of(work, typeof(*i915), gem.retire_work.work);
-
-	i915_retire_requests(i915, 0);
-
-	queue_delayed_work(i915->wq,
-			   &i915->gem.retire_work,
-			   round_jiffies_up_relative(HZ));
-}
-
 static int pm_notifier(struct notifier_block *nb,
 		       unsigned long action,
 		       void *data)
@@ -42,9 +29,6 @@ static int pm_notifier(struct notifier_block *nb,
 	switch (action) {
 	case INTEL_GT_UNPARK:
 		i915_globals_unpark();
-		queue_delayed_work(i915->wq,
-				   &i915->gem.retire_work,
-				   round_jiffies_up_relative(HZ));
 		break;
 
 	case INTEL_GT_PARK:
@@ -59,7 +43,7 @@ static bool switch_to_kernel_context_sync(struct intel_gt *gt)
 {
 	bool result = !intel_gt_is_wedged(gt);
 
-	if (i915_gem_wait_for_idle(gt->i915, I915_GEM_IDLE_TIMEOUT) == -ETIME) {
+	if (intel_gt_wait_for_idle(gt, I915_GEM_IDLE_TIMEOUT) == -ETIME) {
 		/* XXX hide warning from gem_eio */
 		if (i915_modparams.reset) {
 			dev_err(gt->i915->drm.dev,
@@ -219,8 +203,6 @@ void i915_gem_resume(struct drm_i915_private *i915)
 
 void i915_gem_init__pm(struct drm_i915_private *i915)
 {
-	INIT_DELAYED_WORK(&i915->gem.retire_work, retire_work_handler);
-
 	i915->gem.pm_notifier.notifier_call = pm_notifier;
 	blocking_notifier_chain_register(&i915->gt.pm_notifications,
 					 &i915->gem.pm_notifier);
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 bc4c8d763024..e80d309f5427 100644
--- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c
+++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c
@@ -8,6 +8,7 @@
 
 #include "gem/i915_gem_pm.h"
 #include "gt/intel_gt.h"
+#include "gt/intel_gt_requests.h"
 #include "gt/intel_reset.h"
 #include "i915_selftest.h"
 
@@ -304,7 +305,7 @@ create_test_object(struct i915_address_space *vm,
 	int err;
 
 	/* Keep in GEM's good graces */
-	i915_retire_requests(vm->i915, 0);
+	intel_gt_retire_requests(vm->gt, 0);
 
 	size = min(vm->total / 2, 1024ull * DW_PER_PAGE * PAGE_SIZE);
 	size = round_down(size, DW_PER_PAGE * PAGE_SIZE);
@@ -922,7 +923,7 @@ __sseu_finish(const char *name,
 		igt_spinner_end(spin);
 
 	if ((flags & TEST_IDLE) && ret == 0) {
-		ret = i915_gem_wait_for_idle(ce->engine->i915,
+		ret = intel_gt_wait_for_idle(ce->engine->gt,
 					     MAX_SCHEDULE_TIMEOUT);
 		if (ret)
 			return ret;
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 8563af1819c4..382b6aefefec 100644
--- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c
+++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c
@@ -385,7 +385,7 @@ static void disable_retire_worker(struct drm_i915_private *i915)
 {
 	i915_gem_driver_unregister__shrinker(i915);
 	intel_gt_pm_get(&i915->gt);
-	cancel_delayed_work_sync(&i915->gem.retire_work);
+	cancel_delayed_work_sync(&i915->gt.requests.retire_work);
 }
 
 static void restore_retire_worker(struct drm_i915_private *i915)
diff --git a/drivers/gpu/drm/i915/gt/intel_gt.c b/drivers/gpu/drm/i915/gt/intel_gt.c
index c2afffb94474..f38628bfd34c 100644
--- a/drivers/gpu/drm/i915/gt/intel_gt.c
+++ b/drivers/gpu/drm/i915/gt/intel_gt.c
@@ -6,6 +6,7 @@
 #include "i915_drv.h"
 #include "intel_gt.h"
 #include "intel_gt_pm.h"
+#include "intel_gt_requests.h"
 #include "intel_uncore.h"
 
 void intel_gt_init_early(struct intel_gt *gt, struct drm_i915_private *i915)
@@ -20,6 +21,7 @@ void intel_gt_init_early(struct intel_gt *gt, struct drm_i915_private *i915)
 
 	intel_gt_init_hangcheck(gt);
 	intel_gt_init_reset(gt);
+	intel_gt_init_requests(gt);
 	intel_gt_pm_init_early(gt);
 	intel_uc_init_early(&gt->uc);
 }
diff --git a/drivers/gpu/drm/i915/gt/intel_gt_pm.c b/drivers/gpu/drm/i915/gt/intel_gt_pm.c
index aa6cf0152ce7..fa96e1ad7bd8 100644
--- a/drivers/gpu/drm/i915/gt/intel_gt_pm.c
+++ b/drivers/gpu/drm/i915/gt/intel_gt_pm.c
@@ -10,6 +10,7 @@
 #include "intel_engine_pm.h"
 #include "intel_gt.h"
 #include "intel_gt_pm.h"
+#include "intel_gt_requests.h"
 #include "intel_pm.h"
 #include "intel_wakeref.h"
 
@@ -48,6 +49,7 @@ static int __gt_unpark(struct intel_wakeref *wf)
 	i915_pmu_gt_unparked(i915);
 
 	intel_gt_queue_hangcheck(gt);
+	intel_gt_unpark_requests(gt);
 
 	pm_notify(i915, INTEL_GT_UNPARK);
 
@@ -56,13 +58,14 @@ static int __gt_unpark(struct intel_wakeref *wf)
 
 static int __gt_park(struct intel_wakeref *wf)
 {
-	struct drm_i915_private *i915 =
-		container_of(wf, typeof(*i915), gt.wakeref);
-	intel_wakeref_t wakeref = fetch_and_zero(&i915->gt.awake);
+	struct intel_gt *gt = container_of(wf, typeof(*gt), wakeref);
+	intel_wakeref_t wakeref = fetch_and_zero(&gt->awake);
+	struct drm_i915_private *i915 = gt->i915;
 
 	GEM_TRACE("\n");
 
 	pm_notify(i915, INTEL_GT_PARK);
+	intel_gt_park_requests(gt);
 
 	i915_pmu_gt_parked(i915);
 	if (INTEL_GEN(i915) >= 6)
diff --git a/drivers/gpu/drm/i915/gt/intel_gt_requests.c b/drivers/gpu/drm/i915/gt/intel_gt_requests.c
new file mode 100644
index 000000000000..15b2afccb435
--- /dev/null
+++ b/drivers/gpu/drm/i915/gt/intel_gt_requests.c
@@ -0,0 +1,118 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright © 2019 Intel Corporation
+ */
+
+#include "i915_request.h"
+#include "intel_gt.h"
+#include "intel_gt_pm.h"
+#include "intel_gt_requests.h"
+#include "intel_timeline.h"
+
+static void retire_requests(struct intel_timeline *tl)
+{
+	struct i915_request *rq, *rn;
+
+	list_for_each_entry_safe(rq, rn, &tl->requests, link)
+		if (!i915_request_retire(rq))
+			break;
+}
+
+long intel_gt_retire_requests(struct intel_gt *gt, long timeout)
+{
+	struct intel_gt_timelines *timelines = &gt->timelines;
+	struct intel_timeline *tl, *tn;
+	unsigned long active_count = 0;
+	unsigned long flags;
+	LIST_HEAD(free);
+
+	spin_lock_irqsave(&timelines->lock, flags);
+	list_for_each_entry_safe(tl, tn, &timelines->active_list, link) {
+		if (!mutex_trylock(&tl->mutex))
+			continue;
+
+		intel_timeline_get(tl);
+		GEM_BUG_ON(!tl->active_count);
+		tl->active_count++; /* pin the list element */
+		spin_unlock_irqrestore(&timelines->lock, flags);
+
+		if (timeout > 0) {
+			struct dma_fence *fence;
+
+			fence = i915_active_fence_get(&tl->last_request);
+			if (fence) {
+				timeout = dma_fence_wait_timeout(fence,
+								 true,
+								 timeout);
+				dma_fence_put(fence);
+			}
+		}
+
+		retire_requests(tl);
+
+		spin_lock_irqsave(&timelines->lock, flags);
+
+		/* Resume iteration after dropping lock */
+		list_safe_reset_next(tl, tn, link);
+		if (--tl->active_count)
+			active_count += !!rcu_access_pointer(tl->last_request.fence);
+		else
+			list_del(&tl->link);
+
+		mutex_unlock(&tl->mutex);
+
+		/* Defer the final release to after the spinlock */
+		if (refcount_dec_and_test(&tl->kref.refcount)) {
+			GEM_BUG_ON(tl->active_count);
+			list_add(&tl->link, &free);
+		}
+	}
+	spin_unlock_irqrestore(&timelines->lock, flags);
+
+	list_for_each_entry_safe(tl, tn, &free, link)
+		__intel_timeline_free(&tl->kref);
+
+	return active_count ? timeout : 0;
+}
+
+int intel_gt_wait_for_idle(struct intel_gt *gt, long timeout)
+{
+	/* If the device is asleep, we have no requests outstanding */
+	if (!intel_gt_pm_is_awake(gt))
+		return 0;
+
+	while ((timeout = intel_gt_retire_requests(gt, timeout)) > 0) {
+		cond_resched();
+		if (signal_pending(current))
+			return -EINTR;
+	}
+
+	return timeout;
+}
+
+static void retire_work_handler(struct work_struct *work)
+{
+	struct intel_gt *gt =
+		container_of(work, typeof(*gt), requests.retire_work.work);
+
+	intel_gt_retire_requests(gt, 0);
+	schedule_delayed_work(&gt->requests.retire_work,
+			      round_jiffies_up_relative(HZ));
+}
+
+void intel_gt_init_requests(struct intel_gt *gt)
+{
+	INIT_DELAYED_WORK(&gt->requests.retire_work, retire_work_handler);
+}
+
+void intel_gt_park_requests(struct intel_gt *gt)
+{
+	cancel_delayed_work(&gt->requests.retire_work);
+}
+
+void intel_gt_unpark_requests(struct intel_gt *gt)
+{
+	schedule_delayed_work(&gt->requests.retire_work,
+			      round_jiffies_up_relative(HZ));
+}
diff --git a/drivers/gpu/drm/i915/gt/intel_gt_requests.h b/drivers/gpu/drm/i915/gt/intel_gt_requests.h
new file mode 100644
index 000000000000..af07d54ca0af
--- /dev/null
+++ b/drivers/gpu/drm/i915/gt/intel_gt_requests.h
@@ -0,0 +1,19 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright © 2019 Intel Corporation
+ */
+
+#ifndef INTEL_GT_REQUESTS_H
+#define INTEL_GT_REQUESTS_H
+
+struct intel_gt;
+
+long intel_gt_retire_requests(struct intel_gt *gt, long timeout);
+int intel_gt_wait_for_idle(struct intel_gt *gt, long timeout);
+
+void intel_gt_init_requests(struct intel_gt *gt);
+void intel_gt_park_requests(struct intel_gt *gt);
+void intel_gt_unpark_requests(struct intel_gt *gt);
+
+#endif /* INTEL_GT_REQUESTS_H */
diff --git a/drivers/gpu/drm/i915/gt/intel_gt_types.h b/drivers/gpu/drm/i915/gt/intel_gt_types.h
index dc295c196d11..2d040459bd6c 100644
--- a/drivers/gpu/drm/i915/gt/intel_gt_types.h
+++ b/drivers/gpu/drm/i915/gt/intel_gt_types.h
@@ -49,6 +49,17 @@ struct intel_gt {
 		struct list_head hwsp_free_list;
 	} timelines;
 
+	struct intel_gt_requests {
+		/**
+		 * We leave the user IRQ off as much as possible,
+		 * but this means that requests will finish and never
+		 * be retired once the system goes idle. Set a timer to
+		 * fire periodically while the ring is running. When it
+		 * fires, go retire requests.
+		 */
+		struct delayed_work retire_work;
+	} requests;
+
 	struct intel_wakeref wakeref;
 
 	struct list_head closed_vma;
diff --git a/drivers/gpu/drm/i915/gt/selftest_timeline.c b/drivers/gpu/drm/i915/gt/selftest_timeline.c
index b0b0fa5f91de..4490293ec77a 100644
--- a/drivers/gpu/drm/i915/gt/selftest_timeline.c
+++ b/drivers/gpu/drm/i915/gt/selftest_timeline.c
@@ -8,6 +8,7 @@
 
 #include "intel_engine_pm.h"
 #include "intel_gt.h"
+#include "intel_gt_requests.h"
 
 #include "../selftests/i915_random.h"
 #include "../i915_selftest.h"
@@ -641,6 +642,7 @@ static int live_hwsp_alternate(void *arg)
 static int live_hwsp_wrap(void *arg)
 {
 	struct drm_i915_private *i915 = arg;
+	struct intel_gt *gt = &i915->gt;
 	struct intel_engine_cs *engine;
 	struct intel_timeline *tl;
 	enum intel_engine_id id;
@@ -651,7 +653,7 @@ static int live_hwsp_wrap(void *arg)
 	 * foreign GPU references.
 	 */
 
-	tl = intel_timeline_create(&i915->gt, NULL);
+	tl = intel_timeline_create(gt, NULL);
 	if (IS_ERR(tl))
 		return PTR_ERR(tl);
 
@@ -662,7 +664,7 @@ static int live_hwsp_wrap(void *arg)
 	if (err)
 		goto out_free;
 
-	for_each_engine(engine, i915, id) {
+	for_each_engine(engine, gt->i915, id) {
 		const u32 *hwsp_seqno[2];
 		struct i915_request *rq;
 		u32 seqno[2];
@@ -734,7 +736,7 @@ static int live_hwsp_wrap(void *arg)
 			goto out;
 		}
 
-		i915_retire_requests(i915, 0); /* recycle HWSP */
+		intel_gt_retire_requests(gt, 0); /* recycle HWSP */
 	}
 
 out:
diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
index d7410f3f576f..936172f58a65 100644
--- a/drivers/gpu/drm/i915/i915_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
@@ -41,6 +41,7 @@
 
 #include "gem/i915_gem_context.h"
 #include "gt/intel_gt_pm.h"
+#include "gt/intel_gt_requests.h"
 #include "gt/intel_reset.h"
 #include "gt/uc/intel_guc_submission.h"
 
@@ -3601,33 +3602,33 @@ static int
 i915_drop_caches_set(void *data, u64 val)
 {
 	struct drm_i915_private *i915 = data;
+	struct intel_gt *gt = &i915->gt;
 	int ret;
 
 	DRM_DEBUG("Dropping caches: 0x%08llx [0x%08llx]\n",
 		  val, val & DROP_ALL);
 
 	if (val & DROP_RESET_ACTIVE &&
-	    wait_for(intel_engines_are_idle(&i915->gt),
-		     I915_IDLE_ENGINES_TIMEOUT))
-		intel_gt_set_wedged(&i915->gt);
+	    wait_for(intel_engines_are_idle(gt), I915_IDLE_ENGINES_TIMEOUT))
+		intel_gt_set_wedged(gt);
 
 	if (val & DROP_RETIRE)
-		i915_retire_requests(i915, 0);
+		intel_gt_retire_requests(gt, 0);
 
 	if (val & (DROP_IDLE | DROP_ACTIVE)) {
-		ret = i915_gem_wait_for_idle(i915, MAX_SCHEDULE_TIMEOUT);
+		ret = intel_gt_wait_for_idle(gt, MAX_SCHEDULE_TIMEOUT);
 		if (ret)
 			return ret;
 	}
 
 	if (val & DROP_IDLE) {
-		ret = intel_gt_pm_wait_for_idle(&i915->gt);
+		ret = intel_gt_pm_wait_for_idle(gt);
 		if (ret)
 			return ret;
 	}
 
-	if (val & DROP_RESET_ACTIVE && intel_gt_terminally_wedged(&i915->gt))
-		intel_gt_handle_error(&i915->gt, ALL_ENGINES, 0, NULL);
+	if (val & DROP_RESET_ACTIVE && intel_gt_terminally_wedged(gt))
+		intel_gt_handle_error(gt, ALL_ENGINES, 0, NULL);
 
 	fs_reclaim_acquire(GFP_KERNEL);
 	if (val & DROP_BOUND)
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 3d1d652431be..475f260bec69 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -1709,15 +1709,6 @@ struct drm_i915_private {
 
 	struct {
 		struct notifier_block pm_notifier;
-
-		/**
-		 * We leave the user IRQ off as much as possible,
-		 * but this means that requests will finish and never
-		 * be retired once the system goes idle. Set a timer to
-		 * fire periodically while the ring is running. When it
-		 * fires, go retire requests.
-		 */
-		struct delayed_work retire_work;
 	} gem;
 
 	/* For i945gm vblank irq vs. C3 workaround */
@@ -2314,7 +2305,6 @@ void i915_gem_driver_register(struct drm_i915_private *i915);
 void i915_gem_driver_unregister(struct drm_i915_private *i915);
 void i915_gem_driver_remove(struct drm_i915_private *dev_priv);
 void i915_gem_driver_release(struct drm_i915_private *dev_priv);
-int i915_gem_wait_for_idle(struct drm_i915_private *dev_priv, long timeout);
 void i915_gem_suspend(struct drm_i915_private *dev_priv);
 void i915_gem_suspend_late(struct drm_i915_private *dev_priv);
 void i915_gem_resume(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 c5f1c2043f97..c568203558a8 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -890,21 +890,6 @@ void i915_gem_runtime_suspend(struct drm_i915_private *i915)
 	}
 }
 
-int i915_gem_wait_for_idle(struct drm_i915_private *i915, long timeout)
-{
-	/* If the device is asleep, we have no requests outstanding */
-	if (!intel_gt_pm_is_awake(&i915->gt))
-		return 0;
-
-	while ((timeout = i915_retire_requests(i915, timeout)) > 0) {
-		cond_resched();
-		if (signal_pending(current))
-			return -EINTR;
-	}
-
-	return timeout;
-}
-
 struct i915_vma *
 i915_gem_object_ggtt_pin(struct drm_i915_gem_object *obj,
 			 const struct i915_ggtt_view *view,
diff --git a/drivers/gpu/drm/i915/i915_gem_evict.c b/drivers/gpu/drm/i915/i915_gem_evict.c
index 708055a3887e..80302418a590 100644
--- a/drivers/gpu/drm/i915/i915_gem_evict.c
+++ b/drivers/gpu/drm/i915/i915_gem_evict.c
@@ -29,6 +29,7 @@
 #include <drm/i915_drm.h>
 
 #include "gem/i915_gem_context.h"
+#include "gt/intel_gt_requests.h"
 
 #include "i915_drv.h"
 #include "i915_trace.h"
@@ -37,7 +38,7 @@ I915_SELFTEST_DECLARE(static struct igt_evict_ctl {
 	bool fail_if_busy:1;
 } igt_evict_ctl;)
 
-static int ggtt_flush(struct drm_i915_private *i915)
+static int ggtt_flush(struct intel_gt *gt)
 {
 	/*
 	 * Not everything in the GGTT is tracked via vma (otherwise we
@@ -46,7 +47,7 @@ static int ggtt_flush(struct drm_i915_private *i915)
 	 * the hopes that we can then remove contexts and the like only
 	 * bound by their active reference.
 	 */
-	return i915_gem_wait_for_idle(i915, MAX_SCHEDULE_TIMEOUT);
+	return intel_gt_wait_for_idle(gt, MAX_SCHEDULE_TIMEOUT);
 }
 
 static bool
@@ -92,7 +93,6 @@ i915_gem_evict_something(struct i915_address_space *vm,
 			 u64 start, u64 end,
 			 unsigned flags)
 {
-	struct drm_i915_private *dev_priv = vm->i915;
 	struct drm_mm_scan scan;
 	struct list_head eviction_list;
 	struct i915_vma *vma, *next;
@@ -124,7 +124,7 @@ i915_gem_evict_something(struct i915_address_space *vm,
 				    min_size, alignment, cache_level,
 				    start, end, mode);
 
-	i915_retire_requests(vm->i915, 0);
+	intel_gt_retire_requests(vm->gt, 0);
 
 search_again:
 	active = NULL;
@@ -197,7 +197,7 @@ i915_gem_evict_something(struct i915_address_space *vm,
 	if (I915_SELFTEST_ONLY(igt_evict_ctl.fail_if_busy))
 		return -EBUSY;
 
-	ret = ggtt_flush(dev_priv);
+	ret = ggtt_flush(vm->gt);
 	if (ret)
 		return ret;
 
@@ -271,7 +271,7 @@ int i915_gem_evict_for_node(struct i915_address_space *vm,
 	 * a stray pin (preventing eviction) that can only be resolved by
 	 * retiring.
 	 */
-	i915_retire_requests(vm->i915, 0);
+	intel_gt_retire_requests(vm->gt, 0);
 
 	check_color = vm->mm.color_adjust;
 	if (check_color) {
@@ -374,7 +374,7 @@ int i915_gem_evict_vm(struct i915_address_space *vm)
 	 * switch otherwise is ineffective.
 	 */
 	if (i915_is_ggtt(vm)) {
-		ret = ggtt_flush(vm->i915);
+		ret = ggtt_flush(vm->gt);
 		if (ret)
 			return ret;
 	}
diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
index 2b7a4d49b2e6..9b9bbc24e79b 100644
--- a/drivers/gpu/drm/i915/i915_gem_gtt.c
+++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
@@ -38,6 +38,7 @@
 
 #include "display/intel_frontbuffer.h"
 #include "gt/intel_gt.h"
+#include "gt/intel_gt_requests.h"
 
 #include "i915_drv.h"
 #include "i915_scatterlist.h"
@@ -2524,7 +2525,8 @@ void i915_gem_gtt_finish_pages(struct drm_i915_gem_object *obj,
 	struct i915_ggtt *ggtt = &dev_priv->ggtt;
 
 	if (unlikely(ggtt->do_idle_maps)) {
-		if (i915_retire_requests(dev_priv, MAX_SCHEDULE_TIMEOUT)) {
+		if (intel_gt_retire_requests(ggtt->vm.gt,
+					     MAX_SCHEDULE_TIMEOUT)) {
 			DRM_ERROR("Failed to wait for idle; VT'd may hang.\n");
 			/* Wait a bit, in hopes it avoids the hang */
 			udelay(10);
diff --git a/drivers/gpu/drm/i915/i915_request.c b/drivers/gpu/drm/i915/i915_request.c
index 1c5e804c9ca2..2d20ec211cb2 100644
--- a/drivers/gpu/drm/i915/i915_request.c
+++ b/drivers/gpu/drm/i915/i915_request.c
@@ -195,7 +195,7 @@ static void free_capture_list(struct i915_request *request)
 	}
 }
 
-static bool i915_request_retire(struct i915_request *rq)
+bool i915_request_retire(struct i915_request *rq)
 {
 	lockdep_assert_held(&rq->timeline->mutex);
 
@@ -1429,63 +1429,6 @@ long i915_request_wait(struct i915_request *rq,
 	return timeout;
 }
 
-long i915_retire_requests(struct drm_i915_private *i915, long timeout)
-{
-	struct intel_gt_timelines *timelines = &i915->gt.timelines;
-	struct intel_timeline *tl, *tn;
-	unsigned long active_count = 0;
-	unsigned long flags;
-	LIST_HEAD(free);
-
-	spin_lock_irqsave(&timelines->lock, flags);
-	list_for_each_entry_safe(tl, tn, &timelines->active_list, link) {
-		if (!mutex_trylock(&tl->mutex))
-			continue;
-
-		intel_timeline_get(tl);
-		GEM_BUG_ON(!tl->active_count);
-		tl->active_count++; /* pin the list element */
-		spin_unlock_irqrestore(&timelines->lock, flags);
-
-		if (timeout > 0) {
-			struct dma_fence *fence;
-
-			fence = i915_active_fence_get(&tl->last_request);
-			if (fence) {
-				timeout = dma_fence_wait_timeout(fence,
-								 true,
-								 timeout);
-				dma_fence_put(fence);
-			}
-		}
-
-		retire_requests(tl);
-
-		spin_lock_irqsave(&timelines->lock, flags);
-
-		/* Resume iteration after dropping lock */
-		list_safe_reset_next(tl, tn, link);
-		if (--tl->active_count)
-			active_count += !!rcu_access_pointer(tl->last_request.fence);
-		else
-			list_del(&tl->link);
-
-		mutex_unlock(&tl->mutex);
-
-		/* Defer the final release to after the spinlock */
-		if (refcount_dec_and_test(&tl->kref.refcount)) {
-			GEM_BUG_ON(tl->active_count);
-			list_add(&tl->link, &free);
-		}
-	}
-	spin_unlock_irqrestore(&timelines->lock, flags);
-
-	list_for_each_entry_safe(tl, tn, &free, link)
-		__intel_timeline_free(&tl->kref);
-
-	return active_count ? timeout : 0;
-}
-
 #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
 #include "selftests/mock_request.c"
 #include "selftests/i915_request.c"
diff --git a/drivers/gpu/drm/i915/i915_request.h b/drivers/gpu/drm/i915/i915_request.h
index 2a5d682aa6b1..de9ad92145d2 100644
--- a/drivers/gpu/drm/i915/i915_request.h
+++ b/drivers/gpu/drm/i915/i915_request.h
@@ -250,6 +250,7 @@ struct i915_request *__i915_request_commit(struct i915_request *request);
 void __i915_request_queue(struct i915_request *rq,
 			  const struct i915_sched_attr *attr);
 
+bool i915_request_retire(struct i915_request *rq);
 void i915_request_retire_upto(struct i915_request *rq);
 
 static inline struct i915_request *
@@ -439,6 +440,4 @@ static inline bool i915_request_has_nopreempt(const struct i915_request *rq)
 	return unlikely(rq->flags & I915_REQUEST_NOPREEMPT);
 }
 
-long i915_retire_requests(struct drm_i915_private *i915, long timeout);
-
 #endif /* I915_REQUEST_H */
diff --git a/drivers/gpu/drm/i915/selftests/igt_flush_test.c b/drivers/gpu/drm/i915/selftests/igt_flush_test.c
index ed496bd6d84f..7b0939e3f007 100644
--- a/drivers/gpu/drm/i915/selftests/igt_flush_test.c
+++ b/drivers/gpu/drm/i915/selftests/igt_flush_test.c
@@ -4,8 +4,8 @@
  * Copyright © 2018 Intel Corporation
  */
 
-#include "gem/i915_gem_context.h"
 #include "gt/intel_gt.h"
+#include "gt/intel_gt_requests.h"
 
 #include "i915_drv.h"
 #include "i915_selftest.h"
@@ -14,11 +14,12 @@
 
 int igt_flush_test(struct drm_i915_private *i915)
 {
-	int ret = intel_gt_is_wedged(&i915->gt) ? -EIO : 0;
+	struct intel_gt *gt = &i915->gt;
+	int ret = intel_gt_is_wedged(gt) ? -EIO : 0;
 
 	cond_resched();
 
-	if (i915_gem_wait_for_idle(i915, HZ / 5) == -ETIME) {
+	if (intel_gt_wait_for_idle(gt, HZ / 5) == -ETIME) {
 		pr_err("%pS timed out, cancelling all further testing.\n",
 		       __builtin_return_address(0));
 
@@ -26,7 +27,7 @@ int igt_flush_test(struct drm_i915_private *i915)
 			  __builtin_return_address(0));
 		GEM_TRACE_DUMP();
 
-		intel_gt_set_wedged(&i915->gt);
+		intel_gt_set_wedged(gt);
 		ret = -EIO;
 	}
 
diff --git a/drivers/gpu/drm/i915/selftests/igt_live_test.c b/drivers/gpu/drm/i915/selftests/igt_live_test.c
index eae90f97df6c..810b60100c2c 100644
--- a/drivers/gpu/drm/i915/selftests/igt_live_test.c
+++ b/drivers/gpu/drm/i915/selftests/igt_live_test.c
@@ -4,7 +4,8 @@
  * Copyright © 2018 Intel Corporation
  */
 
-#include "../i915_drv.h"
+#include "i915_drv.h"
+#include "gt/intel_gt_requests.h"
 
 #include "../i915_selftest.h"
 #include "igt_flush_test.h"
@@ -23,7 +24,7 @@ int igt_live_test_begin(struct igt_live_test *t,
 	t->func = func;
 	t->name = name;
 
-	err = i915_gem_wait_for_idle(i915, MAX_SCHEDULE_TIMEOUT);
+	err = intel_gt_wait_for_idle(&i915->gt, MAX_SCHEDULE_TIMEOUT);
 	if (err) {
 		pr_err("%s(%s): failed to idle before, with err=%d!",
 		       func, name, err);
diff --git a/drivers/gpu/drm/i915/selftests/mock_gem_device.c b/drivers/gpu/drm/i915/selftests/mock_gem_device.c
index 66cc5634db1c..af0356e66a47 100644
--- a/drivers/gpu/drm/i915/selftests/mock_gem_device.c
+++ b/drivers/gpu/drm/i915/selftests/mock_gem_device.c
@@ -26,6 +26,7 @@
 #include <linux/pm_runtime.h>
 
 #include "gt/intel_gt.h"
+#include "gt/intel_gt_requests.h"
 #include "gt/mock_engine.h"
 
 #include "mock_request.h"
@@ -44,7 +45,7 @@ void mock_device_flush(struct drm_i915_private *i915)
 	do {
 		for_each_engine(engine, i915, id)
 			mock_engine_flush(engine);
-	} while (i915_retire_requests(i915, MAX_SCHEDULE_TIMEOUT));
+	} while (intel_gt_retire_requests(&i915->gt, MAX_SCHEDULE_TIMEOUT));
 }
 
 static void mock_device_release(struct drm_device *dev)
@@ -98,10 +99,6 @@ static void release_dev(struct device *dev)
 	kfree(pdev);
 }
 
-static void mock_retire_work_handler(struct work_struct *work)
-{
-}
-
 static int pm_domain_resume(struct device *dev)
 {
 	return pm_generic_runtime_resume(dev);
@@ -180,8 +177,6 @@ struct drm_i915_private *mock_gem_device(void)
 
 	mock_init_contexts(i915);
 
-	INIT_DELAYED_WORK(&i915->gem.retire_work, mock_retire_work_handler);
-
 	i915->gt.awake = true;
 
 	intel_timelines_init(i915);
-- 
2.23.0

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

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

* [PATCH 21/21] drm/i915: Move global activity tracking from GEM to GT
  2019-09-02  4:02 [PATCH 01/21] drm/i915: Restrict the aliasing-ppgtt to the size of the ggtt Chris Wilson
                   ` (18 preceding siblings ...)
  2019-09-02  4:03 ` [PATCH 20/21] drm/i915: Move request runtime management onto gt Chris Wilson
@ 2019-09-02  4:03 ` Chris Wilson
  2019-09-25  9:55   ` Tvrtko Ursulin
  2019-09-02  4:54 ` ✗ Fi.CI.CHECKPATCH: warning for series starting with [01/21] drm/i915: Restrict the aliasing-ppgtt to the size of the ggtt Patchwork
                   ` (3 subsequent siblings)
  23 siblings, 1 reply; 53+ messages in thread
From: Chris Wilson @ 2019-09-02  4:03 UTC (permalink / raw)
  To: intel-gfx

As our global unpark/park keep track of the number of active users, we
can simply move the accounting from the GEM layer to the base GT layer.
It was placed originally inside GEM to benefit from the 100ms extra
delay on idleness, but that has been eliminated and now there is no
substantive difference between the layers. In moving it, we move another
piece of the puzzle out from underneath struct_mutex.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
---
 drivers/gpu/drm/i915/gem/i915_gem_pm.c | 11 +----------
 drivers/gpu/drm/i915/gt/intel_gt_pm.c  |  5 +++++
 2 files changed, 6 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/i915/gem/i915_gem_pm.c b/drivers/gpu/drm/i915/gem/i915_gem_pm.c
index b459719386e3..5816bdb5bfa2 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_pm.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_pm.c
@@ -10,14 +10,6 @@
 #include "gt/intel_gt_requests.h"
 
 #include "i915_drv.h"
-#include "i915_globals.h"
-
-static void i915_gem_park(struct drm_i915_private *i915)
-{
-	i915_vma_parked(i915);
-
-	i915_globals_park();
-}
 
 static int pm_notifier(struct notifier_block *nb,
 		       unsigned long action,
@@ -28,11 +20,10 @@ static int pm_notifier(struct notifier_block *nb,
 
 	switch (action) {
 	case INTEL_GT_UNPARK:
-		i915_globals_unpark();
 		break;
 
 	case INTEL_GT_PARK:
-		i915_gem_park(i915);
+		i915_vma_parked(i915);
 		break;
 	}
 
diff --git a/drivers/gpu/drm/i915/gt/intel_gt_pm.c b/drivers/gpu/drm/i915/gt/intel_gt_pm.c
index fa96e1ad7bd8..d31ad2d63175 100644
--- a/drivers/gpu/drm/i915/gt/intel_gt_pm.c
+++ b/drivers/gpu/drm/i915/gt/intel_gt_pm.c
@@ -5,6 +5,7 @@
  */
 
 #include "i915_drv.h"
+#include "i915_globals.h"
 #include "i915_params.h"
 #include "intel_context.h"
 #include "intel_engine_pm.h"
@@ -26,6 +27,8 @@ static int __gt_unpark(struct intel_wakeref *wf)
 
 	GEM_TRACE("\n");
 
+	i915_globals_unpark();
+
 	/*
 	 * It seems that the DMC likes to transition between the DC states a lot
 	 * when there are no connected displays (no active power domains) during
@@ -77,6 +80,8 @@ static int __gt_park(struct intel_wakeref *wf)
 	GEM_BUG_ON(!wakeref);
 	intel_display_power_put(i915, POWER_DOMAIN_GT_IRQ, wakeref);
 
+	i915_globals_park();
+
 	return 0;
 }
 
-- 
2.23.0

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

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

* ✗ Fi.CI.CHECKPATCH: warning for series starting with [01/21] drm/i915: Restrict the aliasing-ppgtt to the size of the ggtt
  2019-09-02  4:02 [PATCH 01/21] drm/i915: Restrict the aliasing-ppgtt to the size of the ggtt Chris Wilson
                   ` (19 preceding siblings ...)
  2019-09-02  4:03 ` [PATCH 21/21] drm/i915: Move global activity tracking from GEM to GT Chris Wilson
@ 2019-09-02  4:54 ` Patchwork
  2019-09-02  5:20 ` ✓ Fi.CI.BAT: success " Patchwork
                   ` (2 subsequent siblings)
  23 siblings, 0 replies; 53+ messages in thread
From: Patchwork @ 2019-09-02  4:54 UTC (permalink / raw)
  To: Chris Wilson; +Cc: intel-gfx

== Series Details ==

Series: series starting with [01/21] drm/i915: Restrict the aliasing-ppgtt to the size of the ggtt
URL   : https://patchwork.freedesktop.org/series/66109/
State : warning

== Summary ==

$ dim checkpatch origin/drm-tip
68d6959ea4fd drm/i915: Restrict the aliasing-ppgtt to the size of the ggtt
4fdf35426160 drm/i915: Report aliasing ppgtt size as ggtt size
31b06e8baa53 drm/i915/execlists: Ignore lost completion events
-:11: WARNING:COMMIT_LOG_LONG_LINE: Possible unwrapped commit description (prefer a maximum 75 chars per line)
#11: 
661497512us : process_csb: rcs0 csb[0]: status=0x10008002:0x00000020 [lite-restore]

total: 0 errors, 1 warnings, 0 checks, 133 lines checked
b736ca36e688 drm/i915: Refresh the errno to vmf_fault translations
704d348f6875 drm/i915: Replace obj->pin_global with obj->frontbuffer
-:255: WARNING:PREFER_SEQ_PUTS: Prefer seq_puts to seq_printf
#255: FILE: drivers/gpu/drm/i915/i915_debugfs.c:219:
+		seq_printf(m, " (fb)");

total: 0 errors, 1 warnings, 0 checks, 179 lines checked
6207e25cee7f dma-fence: Serialise signal enabling (dma_fence_enable_sw_signaling)
-:14: ERROR:GIT_COMMIT_ID: Please use git commit description style 'commit <12+ chars of sha1> ("<title line>")' - ie: 'commit 0fc89b6802ba ("dma-fence: Simply wrap dma_fence_signal_locked with dma_fence_signal")'
#14: 
See also 0fc89b6802ba ("dma-fence: Simply wrap dma_fence_signal_locked

total: 1 errors, 0 warnings, 0 checks, 24 lines checked
56fadeb07e9a drm/mm: Pack allocated/scanned boolean into a bitfield
44ed8393bb2c drm/i915: Make shrink/unshrink be atomic
f1ea7eab5fc8 drm/i915: Only track bound elements of the GTT
2419eb0b1436 drm/i915: Make i915_vma.flags atomic_t for mutex reduction
0fc6053b26bd drm/i915/gtt: Make sure the gen6 ppgtt is bound before first use
-:118: CHECK:UNCOMMENTED_DEFINITION: struct mutex definition without comment
#118: FILE: drivers/gpu/drm/i915/i915_gem_gtt.h:430:
+	struct mutex pin_mutex;

total: 0 errors, 0 warnings, 1 checks, 96 lines checked
a39fcbd7b6c4 drm/i915: Mark up address spaces that may need to allocate
ed159fb30e95 drm/i915: Pull i915_vma_pin under the vm->mutex
55ab20749ded drm/i915: Push the i915_active.retire into a worker
19e36732883f drm/i915: Coordinate i915_active with its own mutex
-:1352: CHECK:UNCOMMENTED_DEFINITION: struct mutex definition without comment
#1352: FILE: drivers/gpu/drm/i915/i915_active_types.h:49:
+	struct mutex mutex;

total: 0 errors, 0 warnings, 1 checks, 1509 lines checked
b33339574533 drm/i915: Move idle barrier cleanup into engine-pm
66e5834d5fbb drm/i915: Drop struct_mutex from around i915_retire_requests()
9f0a0ae2882e drm/i915: Remove the GEM idle worker
b071d804c8ef drm/i915: Merge wait_for_timelines with retire_request
cafe9a8c443f drm/i915: Move request runtime management onto gt
-:228: WARNING:FILE_PATH_CHANGES: added, moved or deleted file(s), does MAINTAINERS need updating?
#228: 
new file mode 100644

-:233: WARNING:SPDX_LICENSE_TAG: Missing or malformed SPDX-License-Identifier tag in line 1
#233: FILE: drivers/gpu/drm/i915/gt/intel_gt_requests.c:1:
+/*

-:234: WARNING:SPDX_LICENSE_TAG: Misplaced SPDX-License-Identifier tag - use line 1 instead
#234: FILE: drivers/gpu/drm/i915/gt/intel_gt_requests.c:2:
+ * SPDX-License-Identifier: MIT

-:357: WARNING:SPDX_LICENSE_TAG: Missing or malformed SPDX-License-Identifier tag in line 1
#357: FILE: drivers/gpu/drm/i915/gt/intel_gt_requests.h:1:
+/*

-:358: WARNING:SPDX_LICENSE_TAG: Misplaced SPDX-License-Identifier tag - use line 1 instead
#358: FILE: drivers/gpu/drm/i915/gt/intel_gt_requests.h:2:
+ * SPDX-License-Identifier: MIT

total: 0 errors, 5 warnings, 0 checks, 696 lines checked
da16be36aba9 drm/i915: Move global activity tracking from GEM to GT

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

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

* ✓ Fi.CI.BAT: success for series starting with [01/21] drm/i915: Restrict the aliasing-ppgtt to the size of the ggtt
  2019-09-02  4:02 [PATCH 01/21] drm/i915: Restrict the aliasing-ppgtt to the size of the ggtt Chris Wilson
                   ` (20 preceding siblings ...)
  2019-09-02  4:54 ` ✗ Fi.CI.CHECKPATCH: warning for series starting with [01/21] drm/i915: Restrict the aliasing-ppgtt to the size of the ggtt Patchwork
@ 2019-09-02  5:20 ` Patchwork
  2019-09-02  8:00 ` ✗ Fi.CI.IGT: failure " Patchwork
  2019-09-02  8:52 ` [PATCH 01/21] " Matthew Auld
  23 siblings, 0 replies; 53+ messages in thread
From: Patchwork @ 2019-09-02  5:20 UTC (permalink / raw)
  To: Chris Wilson; +Cc: intel-gfx

== Series Details ==

Series: series starting with [01/21] drm/i915: Restrict the aliasing-ppgtt to the size of the ggtt
URL   : https://patchwork.freedesktop.org/series/66109/
State : success

== Summary ==

CI Bug Log - changes from CI_DRM_6818 -> Patchwork_14255
====================================================

Summary
-------

  **SUCCESS**

  No regressions found.

  External URL: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_14255/

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

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

### IGT changes ###

#### Possible fixes ####

  * igt@gem_exec_gttfill@basic:
    - fi-bsw-kefka:       [SKIP][1] ([fdo#109271]) -> [PASS][2]
   [1]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6818/fi-bsw-kefka/igt@gem_exec_gttfill@basic.html
   [2]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_14255/fi-bsw-kefka/igt@gem_exec_gttfill@basic.html
    - fi-bsw-n3050:       [SKIP][3] ([fdo#109271]) -> [PASS][4]
   [3]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6818/fi-bsw-n3050/igt@gem_exec_gttfill@basic.html
   [4]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_14255/fi-bsw-n3050/igt@gem_exec_gttfill@basic.html

  * igt@gem_exec_suspend@basic-s3:
    - fi-blb-e6850:       [INCOMPLETE][5] ([fdo#107718]) -> [PASS][6]
   [5]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6818/fi-blb-e6850/igt@gem_exec_suspend@basic-s3.html
   [6]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_14255/fi-blb-e6850/igt@gem_exec_suspend@basic-s3.html

  * igt@i915_module_load@reload-with-fault-injection:
    - {fi-icl-guc}:       [DMESG-WARN][7] ([fdo#106107]) -> [PASS][8]
   [7]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6818/fi-icl-guc/igt@i915_module_load@reload-with-fault-injection.html
   [8]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_14255/fi-icl-guc/igt@i915_module_load@reload-with-fault-injection.html

  * igt@kms_frontbuffer_tracking@basic:
    - fi-icl-u3:          [FAIL][9] ([fdo#103167]) -> [PASS][10]
   [9]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6818/fi-icl-u3/igt@kms_frontbuffer_tracking@basic.html
   [10]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_14255/fi-icl-u3/igt@kms_frontbuffer_tracking@basic.html
    - fi-bsw-n3050:       [FAIL][11] ([fdo#103167]) -> [PASS][12]
   [11]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6818/fi-bsw-n3050/igt@kms_frontbuffer_tracking@basic.html
   [12]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_14255/fi-bsw-n3050/igt@kms_frontbuffer_tracking@basic.html

  
#### Warnings ####

  * igt@kms_chamelium@hdmi-hpd-fast:
    - fi-kbl-7500u:       [FAIL][13] ([fdo#111096]) -> [FAIL][14] ([fdo#111407])
   [13]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6818/fi-kbl-7500u/igt@kms_chamelium@hdmi-hpd-fast.html
   [14]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_14255/fi-kbl-7500u/igt@kms_chamelium@hdmi-hpd-fast.html

  
  {name}: This element is suppressed. This means it is ignored when computing
          the status of the difference (SUCCESS, WARNING, or FAILURE).

  [fdo#103167]: https://bugs.freedesktop.org/show_bug.cgi?id=103167
  [fdo#106107]: https://bugs.freedesktop.org/show_bug.cgi?id=106107
  [fdo#107718]: https://bugs.freedesktop.org/show_bug.cgi?id=107718
  [fdo#109271]: https://bugs.freedesktop.org/show_bug.cgi?id=109271
  [fdo#111096]: https://bugs.freedesktop.org/show_bug.cgi?id=111096
  [fdo#111407]: https://bugs.freedesktop.org/show_bug.cgi?id=111407


Participating hosts (50 -> 44)
------------------------------

  Additional (1): fi-kbl-soraka 
  Missing    (7): fi-ilk-m540 fi-hsw-4200u fi-byt-squawks fi-bsw-cyan fi-icl-y fi-byt-clapper fi-bdw-samus 


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

  * CI: CI-20190529 -> None
  * Linux: CI_DRM_6818 -> Patchwork_14255

  CI-20190529: 20190529
  CI_DRM_6818: c914252ce49ad246ccf5b23b8627319fdf5ff995 @ git://anongit.freedesktop.org/gfx-ci/linux
  IGT_5162: e62ea305fdba2a9cd0dadfa527b54529cb0d1438 @ git://anongit.freedesktop.org/xorg/app/intel-gpu-tools
  Patchwork_14255: da16be36aba9d7c66fb817d1773c433593494414 @ git://anongit.freedesktop.org/gfx-ci/linux


== Linux commits ==

da16be36aba9 drm/i915: Move global activity tracking from GEM to GT
cafe9a8c443f drm/i915: Move request runtime management onto gt
b071d804c8ef drm/i915: Merge wait_for_timelines with retire_request
9f0a0ae2882e drm/i915: Remove the GEM idle worker
66e5834d5fbb drm/i915: Drop struct_mutex from around i915_retire_requests()
b33339574533 drm/i915: Move idle barrier cleanup into engine-pm
19e36732883f drm/i915: Coordinate i915_active with its own mutex
55ab20749ded drm/i915: Push the i915_active.retire into a worker
ed159fb30e95 drm/i915: Pull i915_vma_pin under the vm->mutex
a39fcbd7b6c4 drm/i915: Mark up address spaces that may need to allocate
0fc6053b26bd drm/i915/gtt: Make sure the gen6 ppgtt is bound before first use
2419eb0b1436 drm/i915: Make i915_vma.flags atomic_t for mutex reduction
f1ea7eab5fc8 drm/i915: Only track bound elements of the GTT
44ed8393bb2c drm/i915: Make shrink/unshrink be atomic
56fadeb07e9a drm/mm: Pack allocated/scanned boolean into a bitfield
6207e25cee7f dma-fence: Serialise signal enabling (dma_fence_enable_sw_signaling)
704d348f6875 drm/i915: Replace obj->pin_global with obj->frontbuffer
b736ca36e688 drm/i915: Refresh the errno to vmf_fault translations
31b06e8baa53 drm/i915/execlists: Ignore lost completion events
4fdf35426160 drm/i915: Report aliasing ppgtt size as ggtt size
68d6959ea4fd drm/i915: Restrict the aliasing-ppgtt to the size of the ggtt

== Logs ==

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

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

* ✗ Fi.CI.IGT: failure for series starting with [01/21] drm/i915: Restrict the aliasing-ppgtt to the size of the ggtt
  2019-09-02  4:02 [PATCH 01/21] drm/i915: Restrict the aliasing-ppgtt to the size of the ggtt Chris Wilson
                   ` (21 preceding siblings ...)
  2019-09-02  5:20 ` ✓ Fi.CI.BAT: success " Patchwork
@ 2019-09-02  8:00 ` Patchwork
  2019-09-02  8:52 ` [PATCH 01/21] " Matthew Auld
  23 siblings, 0 replies; 53+ messages in thread
From: Patchwork @ 2019-09-02  8:00 UTC (permalink / raw)
  To: Chris Wilson; +Cc: intel-gfx

== Series Details ==

Series: series starting with [01/21] drm/i915: Restrict the aliasing-ppgtt to the size of the ggtt
URL   : https://patchwork.freedesktop.org/series/66109/
State : failure

== Summary ==

CI Bug Log - changes from CI_DRM_6818_full -> Patchwork_14255_full
====================================================

Summary
-------

  **FAILURE**

  Serious unknown changes coming with Patchwork_14255_full absolutely need to be
  verified manually.
  
  If you think the reported changes have nothing to do with the changes
  introduced in Patchwork_14255_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_14255_full:

### IGT changes ###

#### Possible regressions ####

  * igt@gem_busy@close-race:
    - shard-snb:          [PASS][1] -> [FAIL][2]
   [1]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6818/shard-snb7/igt@gem_busy@close-race.html
   [2]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_14255/shard-snb6/igt@gem_busy@close-race.html

  

### Piglit changes ###

#### Warnings ####

  * spec@ext_texture_norm16@render:
    - pig-hsw-4770r:      [CRASH][3] ([fdo#111530]) -> [INCOMPLETE][4] +5 similar issues
   [3]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6818/pig-hsw-4770r/spec@ext_texture_norm16@render.html
   [4]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_14255/pig-hsw-4770r/spec@ext_texture_norm16@render.html

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

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

### IGT changes ###

#### Issues hit ####

  * igt@gem_ctx_shared@exec-single-timeline-bsd:
    - shard-iclb:         [PASS][5] -> [SKIP][6] ([fdo#110841])
   [5]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6818/shard-iclb7/igt@gem_ctx_shared@exec-single-timeline-bsd.html
   [6]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_14255/shard-iclb4/igt@gem_ctx_shared@exec-single-timeline-bsd.html

  * igt@gem_eio@in-flight-contexts-immediate:
    - shard-snb:          [PASS][7] -> [FAIL][8] ([fdo#105957])
   [7]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6818/shard-snb6/igt@gem_eio@in-flight-contexts-immediate.html
   [8]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_14255/shard-snb4/igt@gem_eio@in-flight-contexts-immediate.html

  * igt@gem_exec_schedule@reorder-wide-bsd:
    - shard-iclb:         [PASS][9] -> [SKIP][10] ([fdo#111325]) +6 similar issues
   [9]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6818/shard-iclb5/igt@gem_exec_schedule@reorder-wide-bsd.html
   [10]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_14255/shard-iclb2/igt@gem_exec_schedule@reorder-wide-bsd.html

  * igt@gem_tiled_swapping@non-threaded:
    - shard-iclb:         [PASS][11] -> [INCOMPLETE][12] ([fdo#107713] / [fdo#108686])
   [11]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6818/shard-iclb7/igt@gem_tiled_swapping@non-threaded.html
   [12]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_14255/shard-iclb2/igt@gem_tiled_swapping@non-threaded.html
    - shard-hsw:          [PASS][13] -> [INCOMPLETE][14] ([fdo#103540] / [fdo#108686])
   [13]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6818/shard-hsw4/igt@gem_tiled_swapping@non-threaded.html
   [14]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_14255/shard-hsw1/igt@gem_tiled_swapping@non-threaded.html

  * igt@gem_workarounds@suspend-resume-context:
    - shard-apl:          [PASS][15] -> [DMESG-WARN][16] ([fdo#108566]) +7 similar issues
   [15]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6818/shard-apl3/igt@gem_workarounds@suspend-resume-context.html
   [16]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_14255/shard-apl7/igt@gem_workarounds@suspend-resume-context.html

  * igt@i915_pm_rc6_residency@rc6-accuracy:
    - shard-kbl:          [PASS][17] -> [SKIP][18] ([fdo#109271])
   [17]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6818/shard-kbl3/igt@i915_pm_rc6_residency@rc6-accuracy.html
   [18]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_14255/shard-kbl2/igt@i915_pm_rc6_residency@rc6-accuracy.html

  * igt@i915_pm_rpm@gem-execbuf-stress:
    - shard-apl:          [PASS][19] -> [INCOMPLETE][20] ([fdo#103927])
   [19]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6818/shard-apl6/igt@i915_pm_rpm@gem-execbuf-stress.html
   [20]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_14255/shard-apl1/igt@i915_pm_rpm@gem-execbuf-stress.html

  * igt@i915_pm_rps@reset:
    - shard-apl:          [PASS][21] -> [FAIL][22] ([fdo#102250])
   [21]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6818/shard-apl5/igt@i915_pm_rps@reset.html
   [22]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_14255/shard-apl6/igt@i915_pm_rps@reset.html

  * igt@kms_frontbuffer_tracking@fbc-badstride:
    - shard-iclb:         [PASS][23] -> [FAIL][24] ([fdo#103167]) +2 similar issues
   [23]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6818/shard-iclb3/igt@kms_frontbuffer_tracking@fbc-badstride.html
   [24]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_14255/shard-iclb6/igt@kms_frontbuffer_tracking@fbc-badstride.html

  * igt@kms_psr@psr2_suspend:
    - shard-iclb:         [PASS][25] -> [SKIP][26] ([fdo#109441]) +3 similar issues
   [25]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6818/shard-iclb2/igt@kms_psr@psr2_suspend.html
   [26]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_14255/shard-iclb5/igt@kms_psr@psr2_suspend.html

  * igt@perf@blocking:
    - shard-skl:          [PASS][27] -> [FAIL][28] ([fdo#110728])
   [27]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6818/shard-skl5/igt@perf@blocking.html
   [28]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_14255/shard-skl8/igt@perf@blocking.html

  * igt@prime_busy@hang-bsd2:
    - shard-iclb:         [PASS][29] -> [SKIP][30] ([fdo#109276]) +20 similar issues
   [29]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6818/shard-iclb4/igt@prime_busy@hang-bsd2.html
   [30]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_14255/shard-iclb7/igt@prime_busy@hang-bsd2.html

  
#### Possible fixes ####

  * igt@gem_exec_schedule@preempt-queue-bsd1:
    - shard-iclb:         [SKIP][31] ([fdo#109276]) -> [PASS][32] +22 similar issues
   [31]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6818/shard-iclb5/igt@gem_exec_schedule@preempt-queue-bsd1.html
   [32]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_14255/shard-iclb4/igt@gem_exec_schedule@preempt-queue-bsd1.html

  * igt@gem_exec_schedule@preempt-queue-chain-bsd:
    - shard-iclb:         [SKIP][33] ([fdo#111325]) -> [PASS][34] +3 similar issues
   [33]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6818/shard-iclb4/igt@gem_exec_schedule@preempt-queue-chain-bsd.html
   [34]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_14255/shard-iclb7/igt@gem_exec_schedule@preempt-queue-chain-bsd.html

  * igt@i915_suspend@fence-restore-tiled2untiled:
    - shard-apl:          [DMESG-WARN][35] ([fdo#108566]) -> [PASS][36] +4 similar issues
   [35]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6818/shard-apl1/igt@i915_suspend@fence-restore-tiled2untiled.html
   [36]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_14255/shard-apl3/igt@i915_suspend@fence-restore-tiled2untiled.html

  * igt@kms_cursor_crc@pipe-c-cursor-suspend:
    - shard-skl:          [INCOMPLETE][37] ([fdo#110741]) -> [PASS][38]
   [37]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6818/shard-skl9/igt@kms_cursor_crc@pipe-c-cursor-suspend.html
   [38]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_14255/shard-skl5/igt@kms_cursor_crc@pipe-c-cursor-suspend.html

  * igt@kms_flip@flip-vs-expired-vblank-interruptible:
    - shard-glk:          [FAIL][39] ([fdo#105363]) -> [PASS][40]
   [39]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6818/shard-glk4/igt@kms_flip@flip-vs-expired-vblank-interruptible.html
   [40]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_14255/shard-glk7/igt@kms_flip@flip-vs-expired-vblank-interruptible.html

  * igt@kms_frontbuffer_tracking@fbc-1p-pri-indfb-multidraw:
    - shard-iclb:         [FAIL][41] ([fdo#103167]) -> [PASS][42] +3 similar issues
   [41]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6818/shard-iclb5/igt@kms_frontbuffer_tracking@fbc-1p-pri-indfb-multidraw.html
   [42]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_14255/shard-iclb2/igt@kms_frontbuffer_tracking@fbc-1p-pri-indfb-multidraw.html

  * igt@kms_frontbuffer_tracking@psr-suspend:
    - shard-skl:          [INCOMPLETE][43] ([fdo#104108] / [fdo#106978]) -> [PASS][44]
   [43]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6818/shard-skl7/igt@kms_frontbuffer_tracking@psr-suspend.html
   [44]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_14255/shard-skl1/igt@kms_frontbuffer_tracking@psr-suspend.html

  * igt@kms_plane_alpha_blend@pipe-a-constant-alpha-min:
    - shard-skl:          [FAIL][45] ([fdo#108145]) -> [PASS][46]
   [45]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6818/shard-skl8/igt@kms_plane_alpha_blend@pipe-a-constant-alpha-min.html
   [46]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_14255/shard-skl9/igt@kms_plane_alpha_blend@pipe-a-constant-alpha-min.html

  * igt@kms_plane_alpha_blend@pipe-b-coverage-7efc:
    - shard-skl:          [FAIL][47] ([fdo#108145] / [fdo#110403]) -> [PASS][48]
   [47]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6818/shard-skl3/igt@kms_plane_alpha_blend@pipe-b-coverage-7efc.html
   [48]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_14255/shard-skl3/igt@kms_plane_alpha_blend@pipe-b-coverage-7efc.html

  * igt@kms_plane_lowres@pipe-a-tiling-x:
    - shard-iclb:         [FAIL][49] ([fdo#103166]) -> [PASS][50]
   [49]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6818/shard-iclb5/igt@kms_plane_lowres@pipe-a-tiling-x.html
   [50]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_14255/shard-iclb2/igt@kms_plane_lowres@pipe-a-tiling-x.html

  * igt@kms_psr@psr2_sprite_blt:
    - shard-iclb:         [SKIP][51] ([fdo#109441]) -> [PASS][52] +1 similar issue
   [51]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_6818/shard-iclb1/igt@kms_psr@psr2_sprite_blt.html
   [52]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_14255/shard-iclb2/igt@kms_psr@psr2_sprite_blt.html

  
  [fdo#102250]: https://bugs.freedesktop.org/show_bug.cgi?id=102250
  [fdo#103166]: https://bugs.freedesktop.org/show_bug.cgi?id=103166
  [fdo#103167]: https://bugs.freedesktop.org/show_bug.cgi?id=103167
  [fdo#103540]: https://bugs.freedesktop.org/show_bug.cgi?id=103540
  [fdo#103927]: https://bugs.freedesktop.org/show_bug.cgi?id=103927
  [fdo#104108]: https://bugs.freedesktop.org/show_bug.cgi?id=104108
  [fdo#105363]: https://bugs.freedesktop.org/show_bug.cgi?id=105363
  [fdo#105957]: https://bugs.freedesktop.org/show_bug.cgi?id=105957
  [fdo#106978]: https://bugs.freedesktop.org/show_bug.cgi?id=106978
  [fdo#107713]: https://bugs.freedesktop.org/show_bug.cgi?id=107713
  [fdo#108145]: https://bugs.freedesktop.org/show_bug.cgi?id=108145
  [fdo#108566]: https://bugs.freedesktop.org/show_bug.cgi?id=108566
  [fdo#108686]: https://bugs.freedesktop.org/show_bug.cgi?id=108686
  [fdo#109271]: https://bugs.freedesktop.org/show_bug.cgi?id=109271
  [fdo#109276]: https://bugs.freedesktop.org/show_bug.cgi?id=109276
  [fdo#109441]: https://bugs.freedesktop.org/show_bug.cgi?id=109441
  [fdo#110403]: https://bugs.freedesktop.org/show_bug.cgi?id=110403
  [fdo#110728]: https://bugs.freedesktop.org/show_bug.cgi?id=110728
  [fdo#110741]: https://bugs.freedesktop.org/show_bug.cgi?id=110741
  [fdo#110841]: https://bugs.freedesktop.org/show_bug.cgi?id=110841
  [fdo#111325]: https://bugs.freedesktop.org/show_bug.cgi?id=111325
  [fdo#111530]: https://bugs.freedesktop.org/show_bug.cgi?id=111530


Participating hosts (10 -> 10)
------------------------------

  No changes in participating hosts


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

  * CI: CI-20190529 -> None
  * Linux: CI_DRM_6818 -> Patchwork_14255

  CI-20190529: 20190529
  CI_DRM_6818: c914252ce49ad246ccf5b23b8627319fdf5ff995 @ git://anongit.freedesktop.org/gfx-ci/linux
  IGT_5162: e62ea305fdba2a9cd0dadfa527b54529cb0d1438 @ git://anongit.freedesktop.org/xorg/app/intel-gpu-tools
  Patchwork_14255: da16be36aba9d7c66fb817d1773c433593494414 @ git://anongit.freedesktop.org/gfx-ci/linux
  piglit_4509: fdc5a4ca11124ab8413c7988896eec4c97336694 @ git://anongit.freedesktop.org/piglit

== Logs ==

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

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

* Re: [PATCH 01/21] drm/i915: Restrict the aliasing-ppgtt to the size of the ggtt
  2019-09-02  4:02 [PATCH 01/21] drm/i915: Restrict the aliasing-ppgtt to the size of the ggtt Chris Wilson
                   ` (22 preceding siblings ...)
  2019-09-02  8:00 ` ✗ Fi.CI.IGT: failure " Patchwork
@ 2019-09-02  8:52 ` Matthew Auld
  23 siblings, 0 replies; 53+ messages in thread
From: Matthew Auld @ 2019-09-02  8:52 UTC (permalink / raw)
  To: Chris Wilson; +Cc: Intel Graphics Development, Matthew Auld

On Mon, 2 Sep 2019 at 05:03, Chris Wilson <chris@chris-wilson.co.uk> wrote:
>
> The aliasing-ppgtt is not allowed to be smaller than the ggtt, nor
> should we advertise it as being any bigger, or else we may get sued for
> false advertisement.
>
> Testcase: igt/gem_exec_big
> Fixes: 0b718ba1e884 ("drm/i915/gtt: Downgrade Cherryview back to aliasing-ppgtt")
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> Cc: Matthew Auld <matthew.auld@intel.com>
Reviewed-by: Matthew Auld <matthew.auld@intel.com>
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* Re: [PATCH 02/21] drm/i915: Report aliasing ppgtt size as ggtt size
  2019-09-02  4:02 ` [PATCH 02/21] drm/i915: Report aliasing ppgtt size as ggtt size Chris Wilson
@ 2019-09-02  8:55   ` Matthew Auld
  0 siblings, 0 replies; 53+ messages in thread
From: Matthew Auld @ 2019-09-02  8:55 UTC (permalink / raw)
  To: Chris Wilson; +Cc: Intel Graphics Development

On Mon, 2 Sep 2019 at 05:03, Chris Wilson <chris@chris-wilson.co.uk> wrote:
>
> The aliasing-ppgtt is constrained to be the same size as the Global GTT
> since it aliases the same address space. Simplifying gtt size reporting
> in this case.
>
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Reviewed-by: Matthew Auld <matthew.auld@intel.com>
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* Re: [PATCH 04/21] drm/i915: Refresh the errno to vmf_fault translations
  2019-09-02  4:02 ` [PATCH 04/21] drm/i915: Refresh the errno to vmf_fault translations Chris Wilson
@ 2019-09-03 15:34   ` Abdiel Janulgue
  0 siblings, 0 replies; 53+ messages in thread
From: Abdiel Janulgue @ 2019-09-03 15:34 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 02/09/2019 7.02, Chris Wilson wrote:
> It's been a long time since we accidentally reported -EIO upon wedging,
> it can now only be generated by failure to swap in a page.
> 

Reviewed-by: Abdiel Janulgue <abdiel.janulgue@linux.intel.com>

> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> Cc: Abdiel Janulgue <abdiel.janulgue@linux.intel.com>
> ---
>  drivers/gpu/drm/i915/gem/i915_gem_mman.c | 39 +++++++++---------------
>  1 file changed, 15 insertions(+), 24 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_mman.c b/drivers/gpu/drm/i915/gem/i915_gem_mman.c
> index 261c9bd83f51..82db2b783123 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_mman.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_mman.c
> @@ -287,6 +287,9 @@ vm_fault_t i915_gem_fault(struct vm_fault *vmf)
>  			view.type = I915_GGTT_VIEW_PARTIAL;
>  			vma = i915_gem_object_ggtt_pin(obj, &view, 0, 0, flags);
>  		}
> +
> +		/* The entire mappable GGTT is pinned? Unexpected! */
> +		GEM_BUG_ON(vma == ERR_PTR(-ENOSPC));
>  	}
>  	if (IS_ERR(vma)) {
>  		ret = PTR_ERR(vma);
> @@ -333,23 +336,19 @@ vm_fault_t i915_gem_fault(struct vm_fault *vmf)
>  	i915_gem_object_unpin_pages(obj);
>  err:
>  	switch (ret) {
> -	case -EIO:
> -		/*
> -		 * We eat errors when the gpu is terminally wedged to avoid
> -		 * userspace unduly crashing (gl has no provisions for mmaps to
> -		 * fail). But any other -EIO isn't ours (e.g. swap in failure)
> -		 * and so needs to be reported.
> -		 */
> -		if (!intel_gt_is_wedged(ggtt->vm.gt))
> -			return VM_FAULT_SIGBUS;
> -		/* else, fall through */
> -	case -EAGAIN:
> -		/*
> -		 * EAGAIN means the gpu is hung and we'll wait for the error
> -		 * handler to reset everything when re-faulting in
> -		 * i915_mutex_lock_interruptible.
> -		 */
> +	default:
> +		WARN_ONCE(ret, "unhandled error in %s: %i\n", __func__, ret);
> +		/* fallthrough */
> +	case -EIO: /* shmemfs failure from swap device */
> +	case -EFAULT: /* purged object */
> +		return VM_FAULT_SIGBUS;
> +
> +	case -ENOSPC: /* shmemfs allocation failure */
> +	case -ENOMEM: /* our allocation failure */
> +		return VM_FAULT_OOM;
> +
>  	case 0:
> +	case -EAGAIN:
>  	case -ERESTARTSYS:
>  	case -EINTR:
>  	case -EBUSY:
> @@ -358,14 +357,6 @@ vm_fault_t i915_gem_fault(struct vm_fault *vmf)
>  		 * already did the job.
>  		 */
>  		return VM_FAULT_NOPAGE;
> -	case -ENOMEM:
> -		return VM_FAULT_OOM;
> -	case -ENOSPC:
> -	case -EFAULT:
> -		return VM_FAULT_SIGBUS;
> -	default:
> -		WARN_ONCE(ret, "unhandled error in %s: %i\n", __func__, ret);
> -		return VM_FAULT_SIGBUS;
>  	}
>  }
>  
> 
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* Re: [PATCH 08/21] drm/i915: Make shrink/unshrink be atomic
  2019-09-02  4:02 ` [PATCH 08/21] drm/i915: Make shrink/unshrink be atomic Chris Wilson
@ 2019-09-10 19:54   ` Matthew Auld
  0 siblings, 0 replies; 53+ messages in thread
From: Matthew Auld @ 2019-09-10 19:54 UTC (permalink / raw)
  To: Chris Wilson; +Cc: Intel Graphics Development

On Mon, 2 Sep 2019 at 05:03, Chris Wilson <chris@chris-wilson.co.uk> wrote:
>
> Add an atomic counter and always take the spinlock around the pin/unpin
> events, so that we can perform the list manipulation concurrently.
>
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Reviewed-by: Matthew Auld <matthew.auld@intel.com>
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* Re: [PATCH 10/21] drm/i915: Make i915_vma.flags atomic_t for mutex reduction
  2019-09-02  4:02 ` [PATCH 10/21] drm/i915: Make i915_vma.flags atomic_t for mutex reduction Chris Wilson
@ 2019-09-10 20:06   ` Matthew Auld
  0 siblings, 0 replies; 53+ messages in thread
From: Matthew Auld @ 2019-09-10 20:06 UTC (permalink / raw)
  To: Chris Wilson; +Cc: Intel Graphics Development

On Mon, 2 Sep 2019 at 05:03, Chris Wilson <chris@chris-wilson.co.uk> wrote:
>
> In preparation for reducing struct_mutex stranglehold around the vm,
> make the vma.flags atomic so that we can acquire a pin on the vma
> atomically before deciding if we need to take the mutex.
>
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Reviewed-by: Matthew Auld <matthew.auld@intel.com>
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* Re: [PATCH 11/21] drm/i915/gtt: Make sure the gen6 ppgtt is bound before first use
  2019-09-02  4:02 ` [PATCH 11/21] drm/i915/gtt: Make sure the gen6 ppgtt is bound before first use Chris Wilson
@ 2019-09-10 20:17   ` Matthew Auld
  0 siblings, 0 replies; 53+ messages in thread
From: Matthew Auld @ 2019-09-10 20:17 UTC (permalink / raw)
  To: Chris Wilson; +Cc: Intel Graphics Development

On Mon, 2 Sep 2019 at 05:03, Chris Wilson <chris@chris-wilson.co.uk> wrote:
>
> As we remove the struct_mutex protection from around the vma pinning,
> counters need to be atomic and aware that there may be multiple threads
> simultaneously active.
>
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Reviewed-by: Matthew Auld <matthew.auld@intel.com>
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* Re: [PATCH 13/21] drm/i915: Pull i915_vma_pin under the vm->mutex
  2019-09-02  4:02 ` [PATCH 13/21] drm/i915: Pull i915_vma_pin under the vm->mutex Chris Wilson
@ 2019-09-16 10:13   ` Tvrtko Ursulin
  2019-09-16 11:10     ` Chris Wilson
  2019-09-17 12:37   ` Tvrtko Ursulin
  1 sibling, 1 reply; 53+ messages in thread
From: Tvrtko Ursulin @ 2019-09-16 10:13 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 02/09/2019 05:02, Chris Wilson wrote:
> Replace the struct_mutex requirement for pinning the i915_vma with the
> local vm->mutex instead. Note that the vm->mutex is tainted by the
> shrinker (we require unbinding from inside fs-reclaim) and so we cannot
> allocate while holding that mutex. Instead we have to preallocate
> workers to do allocate and apply the PTE updates after we have we
> reserved their slot in the drm_mm (using fences to order the PTE writes
> with the GPU work and with later unbind).

Can you put some paragraphs into the commit describing the 
infrastructure changes? Like changes to active tracker at least.

Then commentary on effects on shrinker, fences, object management, 
execbuf, basically all major parts of the code which have now been 
fundamentally improved or changed. It would help with review. It's a 
chunky patch/change and I think it needs some theory of operation text.

> 
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> ---
>   drivers/gpu/drm/i915/display/intel_display.c  |  29 +-
>   drivers/gpu/drm/i915/display/intel_fbdev.c    |   8 +-
>   drivers/gpu/drm/i915/display/intel_overlay.c  |  11 +-
>   .../gpu/drm/i915/gem/i915_gem_client_blt.c    |  13 +-
>   drivers/gpu/drm/i915/gem/i915_gem_context.c   |  20 +-
>   drivers/gpu/drm/i915/gem/i915_gem_domain.c    |  19 +-
>   .../gpu/drm/i915/gem/i915_gem_execbuffer.c    |  48 +-
>   drivers/gpu/drm/i915/gem/i915_gem_mman.c      |  24 +-
>   drivers/gpu/drm/i915/gem/i915_gem_object.c    |  33 +-
>   drivers/gpu/drm/i915/gem/i915_gem_object.h    |   5 +
>   drivers/gpu/drm/i915/gem/i915_gem_shrinker.c  |  71 +--
>   drivers/gpu/drm/i915/gem/i915_gem_stolen.c    |   8 +-
>   drivers/gpu/drm/i915/gem/i915_gem_tiling.c    |  15 +-
>   drivers/gpu/drm/i915/gem/i915_gem_userptr.c   |  27 +-
>   .../gpu/drm/i915/gem/selftests/huge_pages.c   |  23 +-
>   .../drm/i915/gem/selftests/i915_gem_context.c |  12 +-
>   .../drm/i915/gem/selftests/i915_gem_mman.c    |   2 -
>   .../drm/i915/gem/selftests/igt_gem_utils.c    |   7 +-
>   drivers/gpu/drm/i915/gt/intel_gt.c            |   5 +-
>   drivers/gpu/drm/i915/gt/intel_ringbuffer.c    |   4 +-
>   drivers/gpu/drm/i915/gt/selftest_hangcheck.c  |  19 +-
>   drivers/gpu/drm/i915/gvt/aperture_gm.c        |  12 +-
>   drivers/gpu/drm/i915/i915_active.c            |  94 +++-
>   drivers/gpu/drm/i915/i915_active.h            |   7 +
>   drivers/gpu/drm/i915/i915_active_types.h      |   5 +
>   drivers/gpu/drm/i915/i915_gem.c               |  94 ++--
>   drivers/gpu/drm/i915/i915_gem_evict.c         |  20 +-
>   drivers/gpu/drm/i915/i915_gem_fence_reg.c     |   5 +-
>   drivers/gpu/drm/i915/i915_gem_gtt.c           | 104 ++--
>   drivers/gpu/drm/i915/i915_gem_gtt.h           |  38 +-
>   drivers/gpu/drm/i915/i915_perf.c              |  32 +-
>   drivers/gpu/drm/i915/i915_vma.c               | 476 ++++++++++++------
>   drivers/gpu/drm/i915/i915_vma.h               |  75 ++-
>   .../gpu/drm/i915/selftests/i915_gem_evict.c   |  36 +-
>   drivers/gpu/drm/i915/selftests/i915_gem_gtt.c |  91 ++--
>   drivers/gpu/drm/i915/selftests/i915_vma.c     |   8 +-
>   36 files changed, 818 insertions(+), 682 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c
> index 5e3b22e3f61d..81c0d7a35c4c 100644
> --- a/drivers/gpu/drm/i915/display/intel_display.c
> +++ b/drivers/gpu/drm/i915/display/intel_display.c
> @@ -2079,7 +2079,6 @@ intel_pin_and_fence_fb_obj(struct drm_framebuffer *fb,
>   	unsigned int pinctl;
>   	u32 alignment;
>   
> -	WARN_ON(!mutex_is_locked(&dev->struct_mutex));
>   	if (WARN_ON(!i915_gem_object_is_framebuffer(obj)))
>   		return ERR_PTR(-EINVAL);
>   
> @@ -2163,8 +2162,6 @@ intel_pin_and_fence_fb_obj(struct drm_framebuffer *fb,
>   
>   void intel_unpin_fb_vma(struct i915_vma *vma, unsigned long flags)
>   {
> -	lockdep_assert_held(&vma->vm->i915->drm.struct_mutex);
> -
>   	i915_gem_object_lock(vma->obj);
>   	if (flags & PLANE_HAS_FENCE)
>   		i915_vma_unpin_fence(vma);
> @@ -3065,12 +3062,10 @@ intel_alloc_initial_plane_obj(struct intel_crtc *crtc,
>   		return false;
>   	}
>   
> -	mutex_lock(&dev->struct_mutex);
>   	obj = i915_gem_object_create_stolen_for_preallocated(dev_priv,
>   							     base_aligned,
>   							     base_aligned,
>   							     size_aligned);
> -	mutex_unlock(&dev->struct_mutex);
>   	if (!obj)
>   		return false;
>   
> @@ -3232,13 +3227,11 @@ intel_find_initial_plane_obj(struct intel_crtc *intel_crtc,
>   	intel_state->color_plane[0].stride =
>   		intel_fb_pitch(fb, 0, intel_state->base.rotation);
>   
> -	mutex_lock(&dev->struct_mutex);
>   	intel_state->vma =
>   		intel_pin_and_fence_fb_obj(fb,
>   					   &intel_state->view,
>   					   intel_plane_uses_fence(intel_state),
>   					   &intel_state->flags);
> -	mutex_unlock(&dev->struct_mutex);
>   	if (IS_ERR(intel_state->vma)) {
>   		DRM_ERROR("failed to pin boot fb on pipe %d: %li\n",
>   			  intel_crtc->pipe, PTR_ERR(intel_state->vma));
> @@ -14364,8 +14357,6 @@ static void fb_obj_bump_render_priority(struct drm_i915_gem_object *obj)
>    * bits.  Some older platforms need special physical address handling for
>    * cursor planes.
>    *
> - * Must be called with struct_mutex held.
> - *
>    * Returns 0 on success, negative error code on failure.
>    */
>   int
> @@ -14422,15 +14413,8 @@ intel_prepare_plane_fb(struct drm_plane *plane,
>   	if (ret)
>   		return ret;
>   
> -	ret = mutex_lock_interruptible(&dev_priv->drm.struct_mutex);
> -	if (ret) {
> -		i915_gem_object_unpin_pages(obj);
> -		return ret;
> -	}
> -
>   	ret = intel_plane_pin_fb(to_intel_plane_state(new_state));
>   
> -	mutex_unlock(&dev_priv->drm.struct_mutex);
>   	i915_gem_object_unpin_pages(obj);
>   	if (ret)
>   		return ret;
> @@ -14479,8 +14463,6 @@ intel_prepare_plane_fb(struct drm_plane *plane,
>    * @old_state: the state from the previous modeset
>    *
>    * Cleans up a framebuffer that has just been removed from a plane.
> - *
> - * Must be called with struct_mutex held.
>    */
>   void
>   intel_cleanup_plane_fb(struct drm_plane *plane,
> @@ -14496,9 +14478,7 @@ intel_cleanup_plane_fb(struct drm_plane *plane,
>   	}
>   
>   	/* Should only be called after a successful intel_prepare_plane_fb()! */
> -	mutex_lock(&dev_priv->drm.struct_mutex);
>   	intel_plane_unpin_fb(to_intel_plane_state(old_state));
> -	mutex_unlock(&dev_priv->drm.struct_mutex);
>   }
>   
>   int
> @@ -14698,7 +14678,6 @@ intel_legacy_cursor_update(struct drm_plane *plane,
>   			   u32 src_w, u32 src_h,
>   			   struct drm_modeset_acquire_ctx *ctx)
>   {
> -	struct drm_i915_private *dev_priv = to_i915(crtc->dev);
>   	struct drm_plane_state *old_plane_state, *new_plane_state;
>   	struct intel_plane *intel_plane = to_intel_plane(plane);
>   	struct intel_crtc_state *crtc_state =
> @@ -14764,13 +14743,9 @@ intel_legacy_cursor_update(struct drm_plane *plane,
>   	if (ret)
>   		goto out_free;
>   
> -	ret = mutex_lock_interruptible(&dev_priv->drm.struct_mutex);
> -	if (ret)
> -		goto out_free;
> -
>   	ret = intel_plane_pin_fb(to_intel_plane_state(new_plane_state));
>   	if (ret)
> -		goto out_unlock;
> +		goto out_free;
>   
>   	intel_frontbuffer_flush(to_intel_frontbuffer(fb), ORIGIN_FLIP);
>   	intel_frontbuffer_track(to_intel_frontbuffer(old_plane_state->fb),
> @@ -14800,8 +14775,6 @@ intel_legacy_cursor_update(struct drm_plane *plane,
>   
>   	intel_plane_unpin_fb(to_intel_plane_state(old_plane_state));
>   
> -out_unlock:
> -	mutex_unlock(&dev_priv->drm.struct_mutex);
>   out_free:
>   	if (new_crtc_state)
>   		intel_crtc_destroy_state(crtc, &new_crtc_state->base);
> diff --git a/drivers/gpu/drm/i915/display/intel_fbdev.c b/drivers/gpu/drm/i915/display/intel_fbdev.c
> index d59eee5c5d9c..a3dea3f2dacd 100644
> --- a/drivers/gpu/drm/i915/display/intel_fbdev.c
> +++ b/drivers/gpu/drm/i915/display/intel_fbdev.c
> @@ -204,7 +204,6 @@ static int intelfb_create(struct drm_fb_helper *helper,
>   		sizes->fb_height = intel_fb->base.height;
>   	}
>   
> -	mutex_lock(&dev->struct_mutex);
>   	wakeref = intel_runtime_pm_get(&dev_priv->runtime_pm);
>   
>   	/* Pin the GGTT vma for our access via info->screen_base.
> @@ -266,7 +265,6 @@ static int intelfb_create(struct drm_fb_helper *helper,
>   	ifbdev->vma_flags = flags;
>   
>   	intel_runtime_pm_put(&dev_priv->runtime_pm, wakeref);
> -	mutex_unlock(&dev->struct_mutex);
>   	vga_switcheroo_client_fb_set(pdev, info);
>   	return 0;
>   
> @@ -274,7 +272,6 @@ static int intelfb_create(struct drm_fb_helper *helper,
>   	intel_unpin_fb_vma(vma, flags);
>   out_unlock:
>   	intel_runtime_pm_put(&dev_priv->runtime_pm, wakeref);
> -	mutex_unlock(&dev->struct_mutex);
>   	return ret;
>   }
>   
> @@ -291,11 +288,8 @@ static void intel_fbdev_destroy(struct intel_fbdev *ifbdev)
>   
>   	drm_fb_helper_fini(&ifbdev->helper);
>   
> -	if (ifbdev->vma) {
> -		mutex_lock(&ifbdev->helper.dev->struct_mutex);
> +	if (ifbdev->vma)
>   		intel_unpin_fb_vma(ifbdev->vma, ifbdev->vma_flags);
> -		mutex_unlock(&ifbdev->helper.dev->struct_mutex);
> -	}
>   
>   	if (ifbdev->fb)
>   		drm_framebuffer_remove(&ifbdev->fb->base);
> diff --git a/drivers/gpu/drm/i915/display/intel_overlay.c b/drivers/gpu/drm/i915/display/intel_overlay.c
> index 29edfc343716..4f36557b3f3b 100644
> --- a/drivers/gpu/drm/i915/display/intel_overlay.c
> +++ b/drivers/gpu/drm/i915/display/intel_overlay.c
> @@ -1303,15 +1303,11 @@ static int get_registers(struct intel_overlay *overlay, bool use_phys)
>   	struct i915_vma *vma;
>   	int err;
>   
> -	mutex_lock(&i915->drm.struct_mutex);
> -
>   	obj = i915_gem_object_create_stolen(i915, PAGE_SIZE);
>   	if (obj == NULL)
>   		obj = i915_gem_object_create_internal(i915, PAGE_SIZE);
> -	if (IS_ERR(obj)) {
> -		err = PTR_ERR(obj);
> -		goto err_unlock;
> -	}
> +	if (IS_ERR(obj))
> +		return PTR_ERR(obj);
>   
>   	vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0, PIN_MAPPABLE);
>   	if (IS_ERR(vma)) {
> @@ -1332,13 +1328,10 @@ static int get_registers(struct intel_overlay *overlay, bool use_phys)
>   	}
>   
>   	overlay->reg_bo = obj;
> -	mutex_unlock(&i915->drm.struct_mutex);
>   	return 0;
>   
>   err_put_bo:
>   	i915_gem_object_put(obj);
> -err_unlock:
> -	mutex_unlock(&i915->drm.struct_mutex);
>   	return err;
>   }
>   
> 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 f99920652751..9e72b42a86f5 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_client_blt.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_client_blt.c
> @@ -152,6 +152,17 @@ static void clear_pages_dma_fence_cb(struct dma_fence *fence,
>   	irq_work_queue(&w->irq_work);
>   }
>   
> +static int move_to_active(struct i915_vma *vma, struct i915_request *rq)
> +{
> +	int err;
> +
> +	err = i915_request_await_active(rq, &vma->active);
> +	if (err)
> +		return err;
> +
> +	return i915_active_ref(&vma->active, rq->timeline, rq);
> +}
> +
>   static void clear_pages_worker(struct work_struct *work)
>   {
>   	struct clear_pages_work *w = container_of(work, typeof(*w), work);
> @@ -211,7 +222,7 @@ static void clear_pages_worker(struct work_struct *work)
>   	 * keep track of the GPU activity within this vma/request, and
>   	 * propagate the signal from the request to w->dma.
>   	 */
> -	err = i915_active_ref(&vma->active, rq->timeline, rq);
> +	err = move_to_active(vma, rq);

What is happening here? It wasn't sufficiently ordered before or 
something changes with this patch?

>   	if (err)
>   		goto out_request;
>   
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context.c b/drivers/gpu/drm/i915/gem/i915_gem_context.c
> index f1c0e5d958f3..653f7275306a 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_context.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_context.c
> @@ -313,8 +313,6 @@ static void i915_gem_context_free(struct i915_gem_context *ctx)
>   	GEM_BUG_ON(!i915_gem_context_is_closed(ctx));
>   
>   	release_hw_id(ctx);
> -	if (ctx->vm)
> -		i915_vm_put(ctx->vm);

Contexts no longer hold a reference to vm? Only vmas?

>   
>   	free_engines(rcu_access_pointer(ctx->engines));
>   	mutex_destroy(&ctx->engines_mutex);
> @@ -379,9 +377,13 @@ void i915_gem_context_release(struct kref *ref)
>   
>   static void context_close(struct i915_gem_context *ctx)
>   {
> +	i915_gem_context_set_closed(ctx);
> +
> +	if (ctx->vm)
> +		i915_vm_close(ctx->vm);

But now closed context mean closed vm, but what about shared vms? Is 
open/close_vm now counted?

> +
>   	mutex_lock(&ctx->mutex);
>   
> -	i915_gem_context_set_closed(ctx);
>   	ctx->file_priv = ERR_PTR(-EBADF);
>   
>   	/*
> @@ -474,7 +476,7 @@ __set_ppgtt(struct i915_gem_context *ctx, struct i915_address_space *vm)
>   
>   	GEM_BUG_ON(old && i915_vm_is_4lvl(vm) != i915_vm_is_4lvl(old));
>   
> -	ctx->vm = i915_vm_get(vm);
> +	ctx->vm = i915_vm_open(vm);
>   	context_apply_all(ctx, __apply_ppgtt, vm);
>   
>   	return old;
> @@ -488,7 +490,7 @@ static void __assign_ppgtt(struct i915_gem_context *ctx,
>   
>   	vm = __set_ppgtt(ctx, vm);
>   	if (vm)
> -		i915_vm_put(vm);
> +		i915_vm_close(vm);
>   }
>   
>   static void __set_timeline(struct intel_timeline **dst,
> @@ -953,7 +955,7 @@ static int get_ppgtt(struct drm_i915_file_private *file_priv,
>   	if (ret < 0)
>   		goto err_unlock;
>   
> -	i915_vm_get(vm);
> +	i915_vm_open(vm);
>   
>   	args->size = 0;
>   	args->value = ret;
> @@ -973,7 +975,7 @@ static void set_ppgtt_barrier(void *data)
>   	if (INTEL_GEN(old->i915) < 8)
>   		gen6_ppgtt_unpin_all(i915_vm_to_ppgtt(old));
>   
> -	i915_vm_put(old);
> +	i915_vm_close(old);
>   }
>   
>   static int emit_ppgtt_update(struct i915_request *rq, void *data)
> @@ -1090,8 +1092,8 @@ static int set_ppgtt(struct drm_i915_file_private *file_priv,
>   				   set_ppgtt_barrier,
>   				   old);
>   	if (err) {
> -		i915_vm_put(__set_ppgtt(ctx, old));
> -		i915_vm_put(old);
> +		i915_vm_close(__set_ppgtt(ctx, old));
> +		i915_vm_close(old);
>   	}
>   
>   unlock:
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_domain.c b/drivers/gpu/drm/i915/gem/i915_gem_domain.c
> index f0c437b6e995..46a23409e1c0 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_domain.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_domain.c
> @@ -202,7 +202,11 @@ int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj,
>   		    i915_gem_valid_gtt_space(vma, cache_level))
>   			continue;
>   
> -		ret = i915_vma_unbind(vma);
> +		ret = mutex_lock_interruptible(&vma->vm->mutex);
> +		if (!ret) {
> +			ret = i915_vma_unbind(vma);
> +			mutex_unlock(&vma->vm->mutex);
> +		}
>   		if (ret)
>   			return ret;
>   
> @@ -288,7 +292,12 @@ int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj,
>   			if (!drm_mm_node_allocated(&vma->node))
>   				continue;
>   
> -			ret = i915_vma_bind(vma, cache_level, PIN_UPDATE);
> +			/* Wait for an earlier async bind */
> +			ret = i915_active_wait(&vma->active);
> +			if (ret)
> +				return ret;
> +
> +			ret = i915_vma_bind(vma, cache_level, PIN_UPDATE, NULL);

Waiting should not be implied in the bind? I am assuming at least there 
will be many callers to bind and like this all of them will have to know 
to do i915_active_wait first?

>   			if (ret)
>   				return ret;
>   		}
> @@ -389,16 +398,11 @@ int i915_gem_set_caching_ioctl(struct drm_device *dev, void *data,
>   	if (ret)
>   		goto out;
>   
> -	ret = mutex_lock_interruptible(&i915->drm.struct_mutex);
> -	if (ret)
> -		goto out;
> -
>   	ret = i915_gem_object_lock_interruptible(obj);
>   	if (ret == 0) {
>   		ret = i915_gem_object_set_cache_level(obj, level);
>   		i915_gem_object_unlock(obj);
>   	}
> -	mutex_unlock(&i915->drm.struct_mutex);
>   
>   out:
>   	i915_gem_object_put(obj);
> @@ -483,6 +487,7 @@ static void i915_gem_object_bump_inactive_ggtt(struct drm_i915_gem_object *obj)
>   		if (!drm_mm_node_allocated(&vma->node))
>   			continue;
>   
> +		GEM_BUG_ON(vma->vm != &i915->ggtt.vm);
>   		list_move_tail(&vma->vm_link, &vma->vm->bound_list);
>   	}
>   	mutex_unlock(&i915->ggtt.vm.mutex);
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> index c049199a1df5..c3dd8ce7e2b7 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> @@ -550,8 +550,11 @@ eb_add_vma(struct i915_execbuffer *eb,
>   		eb_unreserve_vma(vma, vma->exec_flags);
>   
>   		list_add_tail(&vma->exec_link, &eb->unbound);
> -		if (drm_mm_node_allocated(&vma->node))
> +		if (drm_mm_node_allocated(&vma->node)) {
> +			mutex_lock(&vma->vm->mutex);
>   			err = i915_vma_unbind(vma);
> +			mutex_unlock(&vma->vm->mutex);

Should there be __i915_vma_unbind which asserts the lock and 
i915_vma_unbind which takes it?

> +		}
>   		if (unlikely(err))
>   			vma->exec_flags = NULL;
>   	}
> @@ -698,7 +701,9 @@ static int eb_reserve(struct i915_execbuffer *eb)
>   
>   		case 1:
>   			/* Too fragmented, unbind everything and retry */
> +			mutex_lock(&eb->context->vm->mutex);
>   			err = i915_gem_evict_vm(eb->context->vm);
> +			mutex_unlock(&eb->context->vm->mutex);
>   			if (err)
>   				return err;
>   			break;
> @@ -972,7 +977,9 @@ static void reloc_cache_reset(struct reloc_cache *cache)
>   			ggtt->vm.clear_range(&ggtt->vm,
>   					     cache->node.start,
>   					     cache->node.size);
> +			mutex_lock(&ggtt->vm.mutex);
>   			drm_mm_remove_node(&cache->node);
> +			mutex_unlock(&ggtt->vm.mutex);
>   		} else {
>   			i915_vma_unpin((struct i915_vma *)cache->node.mm);
>   		}
> @@ -1047,11 +1054,13 @@ static void *reloc_iomap(struct drm_i915_gem_object *obj,
>   					       PIN_NOEVICT);
>   		if (IS_ERR(vma)) {
>   			memset(&cache->node, 0, sizeof(cache->node));
> +			mutex_lock(&ggtt->vm.mutex);
>   			err = drm_mm_insert_node_in_range
>   				(&ggtt->vm.mm, &cache->node,
>   				 PAGE_SIZE, 0, I915_COLOR_UNEVICTABLE,
>   				 0, ggtt->mappable_end,
>   				 DRM_MM_INSERT_LOW);
> +			mutex_unlock(&ggtt->vm.mutex);
>   			if (err) /* no inactive aperture space, use cpu reloc */
>   				return NULL;
>   		} else {
> @@ -1416,7 +1425,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, target->obj->cache_level,
> -					    PIN_GLOBAL);
> +					    PIN_GLOBAL, NULL);
>   			if (WARN_ONCE(err,
>   				      "Unexpected failure to bind target VMA!"))
>   				return err;
> @@ -2140,35 +2149,6 @@ static struct i915_request *eb_throttle(struct intel_context *ce)
>   	return i915_request_get(rq);
>   }
>   
> -static int
> -__eb_pin_context(struct i915_execbuffer *eb, struct intel_context *ce)
> -{
> -	int err;
> -
> -	if (likely(atomic_inc_not_zero(&ce->pin_count)))
> -		return 0;
> -
> -	err = mutex_lock_interruptible(&eb->i915->drm.struct_mutex);
> -	if (err)
> -		return err;
> -
> -	err = __intel_context_do_pin(ce);
> -	mutex_unlock(&eb->i915->drm.struct_mutex);
> -
> -	return err;
> -}
> -
> -static void
> -__eb_unpin_context(struct i915_execbuffer *eb, struct intel_context *ce)
> -{
> -	if (likely(atomic_add_unless(&ce->pin_count, -1, 1)))
> -		return;
> -
> -	mutex_lock(&eb->i915->drm.struct_mutex);
> -	intel_context_unpin(ce);
> -	mutex_unlock(&eb->i915->drm.struct_mutex);
> -}
> -
>   static int __eb_pin_engine(struct i915_execbuffer *eb, struct intel_context *ce)
>   {
>   	struct intel_timeline *tl;
> @@ -2188,7 +2168,7 @@ static int __eb_pin_engine(struct i915_execbuffer *eb, struct intel_context *ce)
>   	 * GGTT space, so do this first before we reserve a seqno for
>   	 * ourselves.
>   	 */
> -	err = __eb_pin_context(eb, ce);
> +	err = intel_context_pin(ce);
>   	if (err)
>   		return err;
>   
> @@ -2232,7 +2212,7 @@ static int __eb_pin_engine(struct i915_execbuffer *eb, struct intel_context *ce)
>   	intel_context_exit(ce);
>   	intel_context_timeline_unlock(tl);
>   err_unpin:
> -	__eb_unpin_context(eb, ce);
> +	intel_context_unpin(ce);
>   	return err;
>   }
>   
> @@ -2245,7 +2225,7 @@ static void eb_unpin_engine(struct i915_execbuffer *eb)
>   	intel_context_exit(ce);
>   	mutex_unlock(&tl->mutex);
>   
> -	__eb_unpin_context(eb, ce);
> +	intel_context_unpin(ce);
>   }
>   
>   static unsigned int
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_mman.c b/drivers/gpu/drm/i915/gem/i915_gem_mman.c
> index 82db2b783123..9a8c307c5aeb 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_mman.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_mman.c
> @@ -251,16 +251,6 @@ vm_fault_t i915_gem_fault(struct vm_fault *vmf)
>   		goto err_rpm;
>   	}
>   
> -	ret = i915_mutex_lock_interruptible(dev);
> -	if (ret)
> -		goto err_reset;
> -
> -	/* Access to snoopable pages through the GTT is incoherent. */
> -	if (obj->cache_level != I915_CACHE_NONE && !HAS_LLC(i915)) {
> -		ret = -EFAULT;
> -		goto err_unlock;
> -	}
> -
>   	/* Now pin it into the GTT as needed */
>   	vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0,
>   				       PIN_MAPPABLE |
> @@ -293,7 +283,13 @@ vm_fault_t i915_gem_fault(struct vm_fault *vmf)
>   	}
>   	if (IS_ERR(vma)) {
>   		ret = PTR_ERR(vma);
> -		goto err_unlock;
> +		goto err_reset;
> +	}
> +
> +	/* Access to snoopable pages through the GTT is incoherent. */
> +	if (obj->cache_level != I915_CACHE_NONE && !HAS_LLC(i915)) {
> +		ret = -EFAULT;
> +		goto err_unpin;
>   	}
>   
>   	ret = i915_vma_pin_fence(vma);
> @@ -321,14 +317,12 @@ vm_fault_t i915_gem_fault(struct vm_fault *vmf)
>   		intel_wakeref_auto(&i915->ggtt.userfault_wakeref,
>   				   msecs_to_jiffies_timeout(CONFIG_DRM_I915_USERFAULT_AUTOSUSPEND));
>   
> -	i915_vma_set_ggtt_write(vma);
> -
> +	if (write)
> +		i915_vma_set_ggtt_write(vma);
>   err_fence:
>   	i915_vma_unpin_fence(vma);
>   err_unpin:
>   	__i915_vma_unpin(vma);
> -err_unlock:
> -	mutex_unlock(&dev->struct_mutex);
>   err_reset:
>   	intel_gt_reset_unlock(ggtt->vm.gt, srcu);
>   err_rpm:
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.c b/drivers/gpu/drm/i915/gem/i915_gem_object.c
> index 0ef60dae23a7..dbf9be9a79f4 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_object.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_object.c
> @@ -155,21 +155,30 @@ static void __i915_gem_free_objects(struct drm_i915_private *i915,
>   
>   	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
>   	llist_for_each_entry_safe(obj, on, freed, freed) {
> -		struct i915_vma *vma, *vn;
> -
>   		trace_i915_gem_object_destroy(obj);
>   
> -		mutex_lock(&i915->drm.struct_mutex);
> -
> -		list_for_each_entry_safe(vma, vn, &obj->vma.list, obj_link) {
> -			GEM_BUG_ON(i915_vma_is_active(vma));
> -			atomic_and(~I915_VMA_PIN_MASK, &vma->flags);
> -			i915_vma_destroy(vma);
> +		if (!list_empty(&obj->vma.list)) {
> +			struct i915_vma *vma;
> +
> +			/*
> +			 * Note that the vma keeps an object reference while
> +			 * it is active, so it *should* not sleep while we
> +			 * destroy it. Our debug code errs insits it *might*.
> +			 * For the moment, play along.
> +			 */
> +			spin_lock(&obj->vma.lock);
> +			while ((vma = list_first_entry_or_null(&obj->vma.list,
> +							       struct i915_vma,
> +							       obj_link))) {
> +				GEM_BUG_ON(vma->obj != obj);
> +				spin_unlock(&obj->vma.lock);
> +
> +				i915_vma_destroy(vma);
> +
> +				spin_lock(&obj->vma.lock);
> +			}
> +			spin_unlock(&obj->vma.lock);
>   		}
> -		GEM_BUG_ON(!list_empty(&obj->vma.list));
> -		GEM_BUG_ON(!RB_EMPTY_ROOT(&obj->vma.tree));
> -
> -		mutex_unlock(&i915->drm.struct_mutex);
>   
>   		GEM_BUG_ON(atomic_read(&obj->bind_count));
>   		GEM_BUG_ON(obj->userfault_count);
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.h b/drivers/gpu/drm/i915/gem/i915_gem_object.h
> index 29b9eddc4c7f..a78af25dce36 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_object.h
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_object.h
> @@ -106,6 +106,11 @@ static inline void i915_gem_object_lock(struct drm_i915_gem_object *obj)
>   	dma_resv_lock(obj->base.resv, NULL);
>   }
>   
> +static inline bool i915_gem_object_trylock(struct drm_i915_gem_object *obj)
> +{
> +	return dma_resv_trylock(obj->base.resv);
> +}
> +
>   static inline int
>   i915_gem_object_lock_interruptible(struct drm_i915_gem_object *obj)
>   {
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_shrinker.c b/drivers/gpu/drm/i915/gem/i915_gem_shrinker.c
> index d2c05d752909..fd604ea12f7f 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_shrinker.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_shrinker.c
> @@ -16,40 +16,6 @@
>   
>   #include "i915_trace.h"
>   
> -static bool shrinker_lock(struct drm_i915_private *i915,
> -			  unsigned int flags,
> -			  bool *unlock)
> -{
> -	struct mutex *m = &i915->drm.struct_mutex;
> -
> -	switch (mutex_trylock_recursive(m)) {
> -	case MUTEX_TRYLOCK_RECURSIVE:
> -		*unlock = false;
> -		return true;
> -
> -	case MUTEX_TRYLOCK_FAILED:
> -		*unlock = false;
> -		if (flags & I915_SHRINK_ACTIVE &&
> -		    mutex_lock_killable_nested(m, I915_MM_SHRINKER) == 0)
> -			*unlock = true;
> -		return *unlock;
> -
> -	case MUTEX_TRYLOCK_SUCCESS:
> -		*unlock = true;
> -		return true;
> -	}
> -
> -	BUG();
> -}
> -
> -static void shrinker_unlock(struct drm_i915_private *i915, bool unlock)
> -{
> -	if (!unlock)
> -		return;
> -
> -	mutex_unlock(&i915->drm.struct_mutex);
> -}
> -
>   static bool swap_available(void)
>   {
>   	return get_nr_swap_pages() > 0;
> @@ -155,10 +121,6 @@ i915_gem_shrink(struct drm_i915_private *i915,
>   	intel_wakeref_t wakeref = 0;
>   	unsigned long count = 0;
>   	unsigned long scanned = 0;
> -	bool unlock;
> -
> -	if (!shrinker_lock(i915, shrink, &unlock))
> -		return 0;
>   
>   	/*
>   	 * When shrinking the active list, we should also consider active
> @@ -268,8 +230,6 @@ i915_gem_shrink(struct drm_i915_private *i915,
>   	if (shrink & I915_SHRINK_BOUND)
>   		intel_runtime_pm_put(&i915->runtime_pm, wakeref);
>   
> -	shrinker_unlock(i915, unlock);
> -
>   	if (nr_scanned)
>   		*nr_scanned += scanned;
>   	return count;
> @@ -339,19 +299,14 @@ i915_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc)
>   	struct drm_i915_private *i915 =
>   		container_of(shrinker, struct drm_i915_private, mm.shrinker);
>   	unsigned long freed;
> -	bool unlock;
>   
>   	sc->nr_scanned = 0;
>   
> -	if (!shrinker_lock(i915, 0, &unlock))
> -		return SHRINK_STOP;
> -
>   	freed = i915_gem_shrink(i915,
>   				sc->nr_to_scan,
>   				&sc->nr_scanned,
>   				I915_SHRINK_BOUND |
> -				I915_SHRINK_UNBOUND |
> -				I915_SHRINK_WRITEBACK);
> +				I915_SHRINK_UNBOUND);
>   	if (sc->nr_scanned < sc->nr_to_scan && current_is_kswapd()) {
>   		intel_wakeref_t wakeref;
>   
> @@ -366,8 +321,6 @@ i915_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc)
>   		}
>   	}
>   
> -	shrinker_unlock(i915, unlock);
> -
>   	return sc->nr_scanned ? freed : SHRINK_STOP;
>   }
>   
> @@ -384,6 +337,7 @@ i915_gem_shrinker_oom(struct notifier_block *nb, unsigned long event, void *ptr)
>   	freed_pages = 0;
>   	with_intel_runtime_pm(&i915->runtime_pm, wakeref)
>   		freed_pages += i915_gem_shrink(i915, -1UL, NULL,
> +					       I915_SHRINK_ACTIVE |
>   					       I915_SHRINK_BOUND |
>   					       I915_SHRINK_UNBOUND |
>   					       I915_SHRINK_WRITEBACK);
> @@ -419,10 +373,6 @@ i915_gem_shrinker_vmap(struct notifier_block *nb, unsigned long event, void *ptr
>   	struct i915_vma *vma, *next;
>   	unsigned long freed_pages = 0;
>   	intel_wakeref_t wakeref;
> -	bool unlock;
> -
> -	if (!shrinker_lock(i915, 0, &unlock))
> -		return NOTIFY_DONE;
>   
>   	with_intel_runtime_pm(&i915->runtime_pm, wakeref)
>   		freed_pages += i915_gem_shrink(i915, -1UL, NULL,
> @@ -439,15 +389,11 @@ i915_gem_shrinker_vmap(struct notifier_block *nb, unsigned long event, void *ptr
>   		if (!vma->iomap || i915_vma_is_active(vma))
>   			continue;
>   
> -		mutex_unlock(&i915->ggtt.vm.mutex);
>   		if (i915_vma_unbind(vma) == 0)
>   			freed_pages += count;
> -		mutex_lock(&i915->ggtt.vm.mutex);
>   	}
>   	mutex_unlock(&i915->ggtt.vm.mutex);
>   
> -	shrinker_unlock(i915, unlock);
> -
>   	*(unsigned long *)ptr += freed_pages;
>   	return NOTIFY_DONE;
>   }
> @@ -490,22 +436,9 @@ void i915_gem_shrinker_taints_mutex(struct drm_i915_private *i915,
>   
>   	fs_reclaim_acquire(GFP_KERNEL);
>   
> -	/*
> -	 * As we invariably rely on the struct_mutex within the shrinker,
> -	 * but have a complicated recursion dance, taint all the mutexes used
> -	 * within the shrinker with the struct_mutex. For completeness, we
> -	 * taint with all subclass of struct_mutex, even though we should
> -	 * only need tainting by I915_MM_NORMAL to catch possible ABBA
> -	 * deadlocks from using struct_mutex inside @mutex.
> -	 */
> -	mutex_acquire(&i915->drm.struct_mutex.dep_map,
> -		      I915_MM_SHRINKER, 0, _RET_IP_);
> -
>   	mutex_acquire(&mutex->dep_map, 0, 0, _RET_IP_);
>   	mutex_release(&mutex->dep_map, 0, _RET_IP_);
>   
> -	mutex_release(&i915->drm.struct_mutex.dep_map, 0, _RET_IP_);
> -
>   	fs_reclaim_release(GFP_KERNEL);
>   
>   	if (unlock)
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_stolen.c b/drivers/gpu/drm/i915/gem/i915_gem_stolen.c
> index 0d81de1461b4..d2aed728ad8d 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_stolen.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_stolen.c
> @@ -621,8 +621,6 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_i915_private *dev_priv
>   	if (!drm_mm_initialized(&dev_priv->mm.stolen))
>   		return NULL;
>   
> -	lockdep_assert_held(&dev_priv->drm.struct_mutex);
> -
>   	DRM_DEBUG_DRIVER("creating preallocated stolen object: stolen_offset=%pa, gtt_offset=%pa, size=%pa\n",
>   			 &stolen_offset, &gtt_offset, &size);
>   
> @@ -674,21 +672,25 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_i915_private *dev_priv
>   	 * setting up the GTT space. The actual reservation will occur
>   	 * later.
>   	 */
> +	mutex_lock(&ggtt->vm.mutex);
>   	ret = i915_gem_gtt_reserve(&ggtt->vm, &vma->node,
>   				   size, gtt_offset, obj->cache_level,
>   				   0);
>   	if (ret) {
>   		DRM_DEBUG_DRIVER("failed to allocate stolen GTT space\n");
> +		mutex_unlock(&ggtt->vm.mutex);
>   		goto err_pages;
>   	}
>   
>   	GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
>   
> +	GEM_BUG_ON(vma->pages);
>   	vma->pages = obj->mm.pages;
> +	atomic_set(&vma->pages_count, I915_VMA_PAGES_ACTIVE);
> +
>   	set_bit(I915_VMA_GLOBAL_BIND_BIT, __i915_vma_flags(vma));
>   	__i915_vma_set_map_and_fenceable(vma);
>   
> -	mutex_lock(&ggtt->vm.mutex);
>   	list_add_tail(&vma->vm_link, &ggtt->vm.bound_list);
>   	mutex_unlock(&ggtt->vm.mutex);
>   
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_tiling.c b/drivers/gpu/drm/i915/gem/i915_gem_tiling.c
> index ca0c2f451742..b9cfae0e4435 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_tiling.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_tiling.c
> @@ -181,22 +181,25 @@ static int
>   i915_gem_object_fence_prepare(struct drm_i915_gem_object *obj,
>   			      int tiling_mode, unsigned int stride)
>   {
> +	struct i915_ggtt *ggtt = &to_i915(obj->base.dev)->ggtt;
>   	struct i915_vma *vma;
> -	int ret;
> +	int ret = 0;
>   
>   	if (tiling_mode == I915_TILING_NONE)
>   		return 0;
>   
> +	mutex_lock(&ggtt->vm.mutex);
>   	for_each_ggtt_vma(vma, obj) {
>   		if (i915_vma_fence_prepare(vma, tiling_mode, stride))
>   			continue;
>   
>   		ret = i915_vma_unbind(vma);
>   		if (ret)
> -			return ret;
> +			break;
>   	}
> +	mutex_unlock(&ggtt->vm.mutex);
>   
> -	return 0;
> +	return ret;
>   }
>   
>   int
> @@ -212,7 +215,6 @@ i915_gem_object_set_tiling(struct drm_i915_gem_object *obj,
>   
>   	GEM_BUG_ON(!i915_tiling_ok(obj, tiling, stride));
>   	GEM_BUG_ON(!stride ^ (tiling == I915_TILING_NONE));
> -	lockdep_assert_held(&i915->drm.struct_mutex);
>   
>   	if ((tiling | stride) == obj->tiling_and_stride)
>   		return 0;
> @@ -364,12 +366,7 @@ i915_gem_set_tiling_ioctl(struct drm_device *dev, void *data,
>   		}
>   	}
>   
> -	err = mutex_lock_interruptible(&dev->struct_mutex);
> -	if (err)
> -		goto err;
> -
>   	err = i915_gem_object_set_tiling(obj, args->tiling_mode, args->stride);
> -	mutex_unlock(&dev->struct_mutex);
>   
>   	/* We have to maintain this existing ABI... */
>   	args->stride = i915_gem_object_get_stride(obj);
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_userptr.c b/drivers/gpu/drm/i915/gem/i915_gem_userptr.c
> index 74da35611d7c..cd36236e3faf 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_userptr.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_userptr.c
> @@ -92,7 +92,6 @@ userptr_mn_invalidate_range_start(struct mmu_notifier *_mn,
>   	struct i915_mmu_notifier *mn =
>   		container_of(_mn, struct i915_mmu_notifier, mn);
>   	struct interval_tree_node *it;
> -	struct mutex *unlock = NULL;
>   	unsigned long end;
>   	int ret = 0;
>   
> @@ -129,33 +128,13 @@ userptr_mn_invalidate_range_start(struct mmu_notifier *_mn,
>   		}
>   		spin_unlock(&mn->lock);
>   
> -		if (!unlock) {
> -			unlock = &mn->mm->i915->drm.struct_mutex;
> -
> -			switch (mutex_trylock_recursive(unlock)) {
> -			default:
> -			case MUTEX_TRYLOCK_FAILED:
> -				if (mutex_lock_killable_nested(unlock, I915_MM_SHRINKER)) {
> -					i915_gem_object_put(obj);
> -					return -EINTR;
> -				}
> -				/* fall through */
> -			case MUTEX_TRYLOCK_SUCCESS:
> -				break;
> -
> -			case MUTEX_TRYLOCK_RECURSIVE:
> -				unlock = ERR_PTR(-EEXIST);
> -				break;
> -			}
> -		}
> -
>   		ret = i915_gem_object_unbind(obj,
>   					     I915_GEM_OBJECT_UNBIND_ACTIVE);
>   		if (ret == 0)
>   			ret = __i915_gem_object_put_pages(obj, I915_MM_SHRINKER);
>   		i915_gem_object_put(obj);
>   		if (ret)
> -			goto unlock;
> +			return ret;
>   
>   		spin_lock(&mn->lock);
>   
> @@ -168,10 +147,6 @@ userptr_mn_invalidate_range_start(struct mmu_notifier *_mn,
>   	}
>   	spin_unlock(&mn->lock);
>   
> -unlock:
> -	if (!IS_ERR_OR_NULL(unlock))
> -		mutex_unlock(unlock);
> -
>   	return ret;
>   
>   }
> diff --git a/drivers/gpu/drm/i915/gem/selftests/huge_pages.c b/drivers/gpu/drm/i915/gem/selftests/huge_pages.c
> index c5cea4379216..cd771147b41f 100644
> --- a/drivers/gpu/drm/i915/gem/selftests/huge_pages.c
> +++ b/drivers/gpu/drm/i915/gem/selftests/huge_pages.c
> @@ -25,6 +25,17 @@ static const unsigned int page_sizes[] = {
>   	I915_GTT_PAGE_SIZE_4K,
>   };
>   
> +static int unlocked_vma_unbind(struct i915_vma *vma)
> +{
> +	int ret;
> +
> +	mutex_lock(&vma->vm->mutex);
> +	ret = i915_vma_unbind(vma);
> +	mutex_unlock(&vma->vm->mutex);
> +
> +	return ret;
> +}
> +
>   static unsigned int get_largest_page_size(struct drm_i915_private *i915,
>   					  u64 rem)
>   {
> @@ -333,7 +344,11 @@ static int igt_check_page_sizes(struct i915_vma *vma)
>   	struct drm_i915_private *i915 = vma->vm->i915;
>   	unsigned int supported = INTEL_INFO(i915)->page_sizes;
>   	struct drm_i915_gem_object *obj = vma->obj;
> -	int err = 0;
> +	int err;
> +
> +	err = i915_active_wait(&vma->active);
> +	if (err)
> +		return err;
>   
>   	if (!HAS_PAGE_SIZES(i915, vma->page_sizes.sg)) {
>   		pr_err("unsupported page_sizes.sg=%u, supported=%u\n",
> @@ -526,7 +541,7 @@ static int igt_mock_ppgtt_misaligned_dma(void *arg)
>   		 * pages.
>   		 */
>   		for (offset = 4096; offset < page_size; offset += 4096) {
> -			err = i915_vma_unbind(vma);
> +			err = unlocked_vma_unbind(vma);
>   			if (err) {
>   				i915_vma_close(vma);
>   				goto out_unpin;
> @@ -941,7 +956,7 @@ static int __igt_write_huge(struct intel_context *ce,
>   	if (IS_ERR(vma))
>   		return PTR_ERR(vma);
>   
> -	err = i915_vma_unbind(vma);
> +	err = unlocked_vma_unbind(vma);
>   	if (err)
>   		goto out_vma_close;
>   
> @@ -1390,7 +1405,7 @@ static int igt_ppgtt_pin_update(void *arg)
>   			goto out_unpin;
>   		}
>   
> -		err = i915_vma_bind(vma, I915_CACHE_NONE, PIN_UPDATE);
> +		err = i915_vma_bind(vma, I915_CACHE_NONE, PIN_UPDATE, NULL);
>   		if (err)
>   			goto out_unpin;
>   
> 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 da54a718c712..aa67c02ba98c 100644
> --- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c
> +++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c
> @@ -747,10 +747,7 @@ emit_rpcs_query(struct drm_i915_gem_object *obj,
>   	if (err)
>   		goto skip_request;
>   
> -	i915_vma_unpin(batch);
> -	i915_vma_close(batch);
> -	i915_vma_put(batch);
> -
> +	i915_vma_unpin_and_release(&batch, 0);
>   	i915_vma_unpin(vma);
>   
>   	*rq_out = i915_request_get(rq);
> @@ -764,8 +761,7 @@ emit_rpcs_query(struct drm_i915_gem_object *obj,
>   err_request:
>   	i915_request_add(rq);
>   err_batch:
> -	i915_vma_unpin(batch);
> -	i915_vma_put(batch);
> +	i915_vma_unpin_and_release(&batch, 0);
>   err_vma:
>   	i915_vma_unpin(vma);
>   
> @@ -1309,9 +1305,7 @@ static int write_to_scratch(struct i915_gem_context *ctx,
>   	if (err)
>   		goto skip_request;
>   
> -	i915_vma_unpin(vma);
> -	i915_vma_close(vma);
> -	i915_vma_put(vma);
> +	i915_vma_unpin_and_release(&vma, 0);
>   
>   	i915_request_add(rq);
>   
> 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 1d27babff0ce..9c217dfe96a9 100644
> --- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c
> +++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c
> @@ -205,7 +205,6 @@ static int igt_partial_tiling(void *arg)
>   		goto out;
>   	}
>   
> -	mutex_lock(&i915->drm.struct_mutex);
>   	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
>   
>   	if (1) {
> @@ -318,7 +317,6 @@ next_tiling: ;
>   
>   out_unlock:
>   	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
> -	mutex_unlock(&i915->drm.struct_mutex);
>   	i915_gem_object_unpin_pages(obj);
>   out:
>   	i915_gem_object_put(obj);
> 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 ee5dc13a30b3..6718da20f35d 100644
> --- a/drivers/gpu/drm/i915/gem/selftests/igt_gem_utils.c
> +++ b/drivers/gpu/drm/i915/gem/selftests/igt_gem_utils.c
> @@ -154,9 +154,7 @@ int igt_gpu_fill_dw(struct intel_context *ce,
>   
>   	i915_request_add(rq);
>   
> -	i915_vma_unpin(batch);
> -	i915_vma_close(batch);
> -	i915_vma_put(batch);
> +	i915_vma_unpin_and_release(&batch, 0);
>   
>   	return 0;
>   
> @@ -165,7 +163,6 @@ int igt_gpu_fill_dw(struct intel_context *ce,
>   err_request:
>   	i915_request_add(rq);
>   err_batch:
> -	i915_vma_unpin(batch);
> -	i915_vma_put(batch);
> +	i915_vma_unpin_and_release(&batch, 0);
>   	return err;
>   }
> diff --git a/drivers/gpu/drm/i915/gt/intel_gt.c b/drivers/gpu/drm/i915/gt/intel_gt.c
> index d48ec9a76ed1..c2afffb94474 100644
> --- a/drivers/gpu/drm/i915/gt/intel_gt.c
> +++ b/drivers/gpu/drm/i915/gt/intel_gt.c
> @@ -207,11 +207,12 @@ void intel_gt_flush_ggtt_writes(struct intel_gt *gt)
>   
>   	with_intel_runtime_pm(&i915->runtime_pm, wakeref) {
>   		struct intel_uncore *uncore = gt->uncore;
> +		unsigned long flags;
>   
> -		spin_lock_irq(&uncore->lock);
> +		spin_lock_irqsave(&uncore->lock, flags);
>   		intel_uncore_posting_read_fw(uncore,
>   					     RING_HEAD(RENDER_RING_BASE));
> -		spin_unlock_irq(&uncore->lock);
> +		spin_unlock_irqrestore(&uncore->lock, flags);
>   	}
>   }
>   
> diff --git a/drivers/gpu/drm/i915/gt/intel_ringbuffer.c b/drivers/gpu/drm/i915/gt/intel_ringbuffer.c
> index ac55a0d054bd..855e97ccaf9f 100644
> --- a/drivers/gpu/drm/i915/gt/intel_ringbuffer.c
> +++ b/drivers/gpu/drm/i915/gt/intel_ringbuffer.c
> @@ -1336,15 +1336,13 @@ void intel_ring_free(struct kref *ref)
>   {
>   	struct intel_ring *ring = container_of(ref, typeof(*ring), ref);
>   
> -	i915_vma_close(ring->vma);
>   	i915_vma_put(ring->vma);
> -
>   	kfree(ring);
>   }
>   
>   static void __ring_context_fini(struct intel_context *ce)
>   {
> -	i915_gem_object_put(ce->state->obj);
> +	i915_vma_put(ce->state);
>   }
>   
>   static void ring_context_destroy(struct kref *ref)
> diff --git a/drivers/gpu/drm/i915/gt/selftest_hangcheck.c b/drivers/gpu/drm/i915/gt/selftest_hangcheck.c
> index a0098fc35921..e53eea1050f8 100644
> --- a/drivers/gpu/drm/i915/gt/selftest_hangcheck.c
> +++ b/drivers/gpu/drm/i915/gt/selftest_hangcheck.c
> @@ -1127,15 +1127,14 @@ static int evict_vma(void *data)
>   {
>   	struct evict_vma *arg = data;
>   	struct i915_address_space *vm = arg->vma->vm;
> -	struct drm_i915_private *i915 = vm->i915;
>   	struct drm_mm_node evict = arg->vma->node;
>   	int err;
>   
>   	complete(&arg->completion);
>   
> -	mutex_lock(&i915->drm.struct_mutex);
> +	mutex_lock(&vm->mutex);
>   	err = i915_gem_evict_for_node(vm, &evict, 0);
> -	mutex_unlock(&i915->drm.struct_mutex);
> +	mutex_unlock(&vm->mutex);
>   
>   	return err;
>   }
> @@ -1143,39 +1142,33 @@ static int evict_vma(void *data)
>   static int evict_fence(void *data)
>   {
>   	struct evict_vma *arg = data;
> -	struct drm_i915_private *i915 = arg->vma->vm->i915;
>   	int err;
>   
>   	complete(&arg->completion);
>   
> -	mutex_lock(&i915->drm.struct_mutex);
> -
>   	/* Mark the fence register as dirty to force the mmio update. */
>   	err = i915_gem_object_set_tiling(arg->vma->obj, I915_TILING_Y, 512);
>   	if (err) {
>   		pr_err("Invalid Y-tiling settings; err:%d\n", err);
> -		goto out_unlock;
> +		return err;
>   	}
>   
>   	err = i915_vma_pin(arg->vma, 0, 0, PIN_GLOBAL | PIN_MAPPABLE);
>   	if (err) {
>   		pr_err("Unable to pin vma for Y-tiled fence; err:%d\n", err);
> -		goto out_unlock;
> +		return err;
>   	}
>   
>   	err = i915_vma_pin_fence(arg->vma);
>   	i915_vma_unpin(arg->vma);
>   	if (err) {
>   		pr_err("Unable to pin Y-tiled fence; err:%d\n", err);
> -		goto out_unlock;
> +		return err;
>   	}
>   
>   	i915_vma_unpin_fence(arg->vma);
>   
> -out_unlock:
> -	mutex_unlock(&i915->drm.struct_mutex);
> -
> -	return err;
> +	return 0;
>   }
>   
>   static int __igt_reset_evict_vma(struct intel_gt *gt,
> diff --git a/drivers/gpu/drm/i915/gvt/aperture_gm.c b/drivers/gpu/drm/i915/gvt/aperture_gm.c
> index 5ff2437b2998..d996bbc7ea59 100644
> --- a/drivers/gpu/drm/i915/gvt/aperture_gm.c
> +++ b/drivers/gpu/drm/i915/gvt/aperture_gm.c
> @@ -61,14 +61,14 @@ static int alloc_gm(struct intel_vgpu *vgpu, bool high_gm)
>   		flags = PIN_MAPPABLE;
>   	}
>   
> -	mutex_lock(&dev_priv->drm.struct_mutex);
> +	mutex_lock(&dev_priv->ggtt.vm.mutex);
>   	mmio_hw_access_pre(dev_priv);
>   	ret = i915_gem_gtt_insert(&dev_priv->ggtt.vm, node,
>   				  size, I915_GTT_PAGE_SIZE,
>   				  I915_COLOR_UNEVICTABLE,
>   				  start, end, flags);
>   	mmio_hw_access_post(dev_priv);
> -	mutex_unlock(&dev_priv->drm.struct_mutex);
> +	mutex_unlock(&dev_priv->ggtt.vm.mutex);
>   	if (ret)
>   		gvt_err("fail to alloc %s gm space from host\n",
>   			high_gm ? "high" : "low");
> @@ -98,9 +98,9 @@ static int alloc_vgpu_gm(struct intel_vgpu *vgpu)
>   
>   	return 0;
>   out_free_aperture:
> -	mutex_lock(&dev_priv->drm.struct_mutex);
> +	mutex_lock(&dev_priv->ggtt.vm.mutex);
>   	drm_mm_remove_node(&vgpu->gm.low_gm_node);
> -	mutex_unlock(&dev_priv->drm.struct_mutex);
> +	mutex_unlock(&dev_priv->ggtt.vm.mutex);
>   	return ret;
>   }
>   
> @@ -108,10 +108,10 @@ static void free_vgpu_gm(struct intel_vgpu *vgpu)
>   {
>   	struct drm_i915_private *dev_priv = vgpu->gvt->dev_priv;
>   
> -	mutex_lock(&dev_priv->drm.struct_mutex);
> +	mutex_lock(&dev_priv->ggtt.vm.mutex);
>   	drm_mm_remove_node(&vgpu->gm.low_gm_node);
>   	drm_mm_remove_node(&vgpu->gm.high_gm_node);
> -	mutex_unlock(&dev_priv->drm.struct_mutex);
> +	mutex_unlock(&dev_priv->ggtt.vm.mutex);
>   }
>   
>   /**
> diff --git a/drivers/gpu/drm/i915/i915_active.c b/drivers/gpu/drm/i915/i915_active.c
> index 6a447f1d0110..6a37ed52957a 100644
> --- a/drivers/gpu/drm/i915/i915_active.c
> +++ b/drivers/gpu/drm/i915/i915_active.c
> @@ -146,6 +146,7 @@ __active_retire(struct i915_active *ref)
>   	if (!retire)
>   		return;
>   
> +	GEM_BUG_ON(rcu_access_pointer(ref->excl));
>   	rbtree_postorder_for_each_entry_safe(it, n, &root, node) {
>   		GEM_BUG_ON(i915_active_request_isset(&it->base));
>   		kmem_cache_free(global.slab_cache, it);
> @@ -245,6 +246,8 @@ void __i915_active_init(struct drm_i915_private *i915,
>   	ref->flags = 0;
>   	ref->active = active;
>   	ref->retire = retire;
> +
> +	ref->excl = NULL;
>   	ref->tree = RB_ROOT;
>   	ref->cache = NULL;
>   	init_llist_head(&ref->preallocated_barriers);
> @@ -341,6 +344,45 @@ int i915_active_ref(struct i915_active *ref,
>   	return err;
>   }
>   
> +static void excl_cb(struct dma_fence *f, struct dma_fence_cb *cb)
> +{
> +	struct i915_active *ref = container_of(cb, typeof(*ref), excl_cb);
> +
> +	RCU_INIT_POINTER(ref->excl, NULL);
> +	dma_fence_put(f);
> +
> +	active_retire(ref);
> +}
> +
> +void i915_active_set_exclusive(struct i915_active *ref, struct dma_fence *f)
> +{
> +	GEM_BUG_ON(i915_active_is_idle(ref));
> +
> +	dma_fence_get(f);
> +
> +	rcu_read_lock();
> +	if (rcu_access_pointer(ref->excl)) {
> +		struct dma_fence *old;
> +
> +		old = dma_fence_get_rcu_safe(&ref->excl);
> +		if (old) {
> +			if (dma_fence_remove_callback(old, &ref->excl_cb))
> +				atomic_dec(&ref->count);
> +			dma_fence_put(old);
> +		}
> +	}
> +	rcu_read_unlock();
> +
> +	atomic_inc(&ref->count);
> +	rcu_assign_pointer(ref->excl, f);
> +
> +	if (dma_fence_add_callback(f, &ref->excl_cb, excl_cb)) {
> +		RCU_INIT_POINTER(ref->excl, NULL);
> +		atomic_dec(&ref->count);
> +		dma_fence_put(f);

Is this some silent failure path?

> +	}
> +}
> +
>   int i915_active_acquire(struct i915_active *ref)
>   {
>   	int err;
> @@ -399,6 +441,25 @@ void i915_active_ungrab(struct i915_active *ref)
>   	__active_ungrab(ref);
>   }
>   
> +static int excl_wait(struct i915_active *ref)
> +{
> +	struct dma_fence *old;
> +	int err = 0;
> +
> +	if (!rcu_access_pointer(ref->excl))
> +		return 0;
> +
> +	rcu_read_lock();
> +	old = dma_fence_get_rcu_safe(&ref->excl);
> +	rcu_read_unlock();
> +	if (old) {
> +		err = dma_fence_wait(old, true);
> +		dma_fence_put(old);
> +	}
> +
> +	return err;
> +}
> +
>   int i915_active_wait(struct i915_active *ref)
>   {
>   	struct active_node *it, *n;
> @@ -419,6 +480,10 @@ int i915_active_wait(struct i915_active *ref)
>   		return 0;
>   	}
>   
> +	err = excl_wait(ref);
> +	if (err)
> +		goto out;
> +
>   	rbtree_postorder_for_each_entry_safe(it, n, &ref->tree, node) {
>   		if (is_barrier(&it->base)) { /* unconnected idle-barrier */
>   			err = -EBUSY;
> @@ -430,6 +495,7 @@ int i915_active_wait(struct i915_active *ref)
>   			break;
>   	}
>   
> +out:
>   	__active_retire(ref);
>   	if (err)
>   		return err;
> @@ -454,26 +520,22 @@ int i915_request_await_active_request(struct i915_request *rq,
>   
>   int i915_request_await_active(struct i915_request *rq, struct i915_active *ref)
>   {
> -	struct active_node *it, *n;
> -	int err;
> -
> -	if (RB_EMPTY_ROOT(&ref->tree))
> -		return 0;
> +	int err = 0;
>   
> -	/* await allocates and so we need to avoid hitting the shrinker */
> -	err = i915_active_acquire(ref);
> -	if (err)
> -		return err;
> +	if (rcu_access_pointer(ref->excl)) {
> +		struct dma_fence *fence;
>   
> -	mutex_lock(&ref->mutex);
> -	rbtree_postorder_for_each_entry_safe(it, n, &ref->tree, node) {
> -		err = i915_request_await_active_request(rq, &it->base);
> -		if (err)
> -			break;
> +		rcu_read_lock();
> +		fence = dma_fence_get_rcu_safe(&ref->excl);
> +		rcu_read_unlock();
> +		if (fence) {
> +			err = i915_request_await_dma_fence(rq, fence);
> +			dma_fence_put(fence);
> +		}
>   	}
> -	mutex_unlock(&ref->mutex);
>   
> -	i915_active_release(ref);
> +	/* In the future we may choose to await on all fences */
> +

This is completely changed to just wait on the "exclusive" fence? What 
about the tree of nodes? It's still in the data structure and managed 
elsewhere. Not needed any more until "future" from the comment?

This much for now.

Regards,

Tvrtko

>   	return err;
>   }
>   
> diff --git a/drivers/gpu/drm/i915/i915_active.h b/drivers/gpu/drm/i915/i915_active.h
> index f95058f99057..af3d536e26fd 100644
> --- a/drivers/gpu/drm/i915/i915_active.h
> +++ b/drivers/gpu/drm/i915/i915_active.h
> @@ -373,6 +373,13 @@ int i915_active_ref(struct i915_active *ref,
>   		    struct intel_timeline *tl,
>   		    struct i915_request *rq);
>   
> +void i915_active_set_exclusive(struct i915_active *ref, struct dma_fence *f);
> +
> +static inline bool i915_active_has_exclusive(struct i915_active *ref)
> +{
> +	return rcu_access_pointer(ref->excl);
> +}
> +
>   int i915_active_wait(struct i915_active *ref);
>   
>   int i915_request_await_active(struct i915_request *rq,
> diff --git a/drivers/gpu/drm/i915/i915_active_types.h b/drivers/gpu/drm/i915/i915_active_types.h
> index 1854e7d168c1..86e7a232ea3c 100644
> --- a/drivers/gpu/drm/i915/i915_active_types.h
> +++ b/drivers/gpu/drm/i915/i915_active_types.h
> @@ -8,6 +8,7 @@
>   #define _I915_ACTIVE_TYPES_H_
>   
>   #include <linux/atomic.h>
> +#include <linux/dma-fence.h>
>   #include <linux/llist.h>
>   #include <linux/mutex.h>
>   #include <linux/rbtree.h>
> @@ -51,6 +52,10 @@ struct i915_active {
>   	struct mutex mutex;
>   	atomic_t count;
>   
> +	/* Preallocated "exclusive" node */
> +	struct dma_fence __rcu *excl;
> +	struct dma_fence_cb excl_cb;

What for and how is the "exclusive" node used for? Where it is 
pre-allocated and by whom? Why it is in double quotes?

> +
>   	unsigned long flags;
>   #define I915_ACTIVE_GRAB_BIT 0
>   
> diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
> index 814f62fca727..3eed2efa8d13 100644
> --- a/drivers/gpu/drm/i915/i915_gem.c
> +++ b/drivers/gpu/drm/i915/i915_gem.c
> @@ -62,20 +62,31 @@
>   #include "intel_pm.h"
>   
>   static int
> -insert_mappable_node(struct i915_ggtt *ggtt,
> -                     struct drm_mm_node *node, u32 size)
> +insert_mappable_node(struct i915_ggtt *ggtt, struct drm_mm_node *node, u32 size)
>   {
> +	int err;
> +
> +	err = mutex_lock_interruptible(&ggtt->vm.mutex);
> +	if (err)
> +		return err;
> +
>   	memset(node, 0, sizeof(*node));
> -	return drm_mm_insert_node_in_range(&ggtt->vm.mm, node,
> -					   size, 0, I915_COLOR_UNEVICTABLE,
> -					   0, ggtt->mappable_end,
> -					   DRM_MM_INSERT_LOW);
> +	err = drm_mm_insert_node_in_range(&ggtt->vm.mm, node,
> +					  size, 0, I915_COLOR_UNEVICTABLE,
> +					  0, ggtt->mappable_end,
> +					  DRM_MM_INSERT_LOW);
> +
> +	mutex_unlock(&ggtt->vm.mutex);
> +
> +	return err;
>   }
>   
>   static void
> -remove_mappable_node(struct drm_mm_node *node)
> +remove_mappable_node(struct i915_ggtt *ggtt, struct drm_mm_node *node)
>   {
> +	mutex_lock(&ggtt->vm.mutex);
>   	drm_mm_remove_node(node);
> +	mutex_unlock(&ggtt->vm.mutex);
>   }
>   
>   int
> @@ -87,7 +98,8 @@ i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data,
>   	struct i915_vma *vma;
>   	u64 pinned;
>   
> -	mutex_lock(&ggtt->vm.mutex);
> +	if (mutex_lock_interruptible(&ggtt->vm.mutex))
> +		return -EINTR;
>   
>   	pinned = ggtt->vm.reserved;
>   	list_for_each_entry(vma, &ggtt->vm.bound_list, vm_link)
> @@ -109,20 +121,31 @@ int i915_gem_object_unbind(struct drm_i915_gem_object *obj,
>   	LIST_HEAD(still_in_list);
>   	int ret = 0;
>   
> -	lockdep_assert_held(&obj->base.dev->struct_mutex);
> -
>   	spin_lock(&obj->vma.lock);
>   	while (!ret && (vma = list_first_entry_or_null(&obj->vma.list,
>   						       struct i915_vma,
>   						       obj_link))) {
> +		struct i915_address_space *vm = vma->vm;
> +
> +		ret = -EBUSY;
> +		if (!i915_vm_tryopen(vm))
> +			break;
> +
>   		list_move_tail(&vma->obj_link, &still_in_list);
>   		spin_unlock(&obj->vma.lock);
>   
> -		ret = -EBUSY;
>   		if (flags & I915_GEM_OBJECT_UNBIND_ACTIVE ||
> -		    !i915_vma_is_active(vma))
> -			ret = i915_vma_unbind(vma);
> +		    !i915_vma_is_active(vma)) {
> +			struct i915_address_space *vm = vma->vm;
> +
> +			ret = mutex_lock_interruptible(&vm->mutex);
> +			if (!ret) {
> +				ret = i915_vma_unbind(vma);
> +				mutex_unlock(&vm->mutex);
> +			}
> +		}
>   
> +		i915_vm_close(vm);
>   		spin_lock(&obj->vma.lock);
>   	}
>   	list_splice(&still_in_list, &obj->vma.list);
> @@ -338,10 +361,6 @@ i915_gem_gtt_pread(struct drm_i915_gem_object *obj,
>   	u64 remain, offset;
>   	int ret;
>   
> -	ret = mutex_lock_interruptible(&i915->drm.struct_mutex);
> -	if (ret)
> -		return ret;
> -
>   	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
>   	vma = ERR_PTR(-ENODEV);
>   	if (!i915_gem_object_is_tiled(obj))
> @@ -355,12 +374,10 @@ i915_gem_gtt_pread(struct drm_i915_gem_object *obj,
>   	} else {
>   		ret = insert_mappable_node(ggtt, &node, PAGE_SIZE);
>   		if (ret)
> -			goto out_unlock;
> +			goto out_rpm;
>   		GEM_BUG_ON(!drm_mm_node_allocated(&node));
>   	}
>   
> -	mutex_unlock(&i915->drm.struct_mutex);
> -
>   	ret = i915_gem_object_lock_interruptible(obj);
>   	if (ret)
>   		goto out_unpin;
> @@ -414,17 +431,14 @@ i915_gem_gtt_pread(struct drm_i915_gem_object *obj,
>   
>   	i915_gem_object_unlock_fence(obj, fence);
>   out_unpin:
> -	mutex_lock(&i915->drm.struct_mutex);
>   	if (drm_mm_node_allocated(&node)) {
>   		ggtt->vm.clear_range(&ggtt->vm, node.start, node.size);
> -		remove_mappable_node(&node);
> +		remove_mappable_node(ggtt, &node);
>   	} else {
>   		i915_vma_unpin(vma);
>   	}
> -out_unlock:
> +out_rpm:
>   	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
> -	mutex_unlock(&i915->drm.struct_mutex);
> -
>   	return ret;
>   }
>   
> @@ -531,10 +545,6 @@ i915_gem_gtt_pwrite_fast(struct drm_i915_gem_object *obj,
>   	void __user *user_data;
>   	int ret;
>   
> -	ret = mutex_lock_interruptible(&i915->drm.struct_mutex);
> -	if (ret)
> -		return ret;
> -
>   	if (i915_gem_object_has_struct_page(obj)) {
>   		/*
>   		 * Avoid waking the device up if we can fallback, as
> @@ -544,10 +554,8 @@ i915_gem_gtt_pwrite_fast(struct drm_i915_gem_object *obj,
>   		 * using the cache bypass of indirect GGTT access.
>   		 */
>   		wakeref = intel_runtime_pm_get_if_in_use(rpm);
> -		if (!wakeref) {
> -			ret = -EFAULT;
> -			goto out_unlock;
> -		}
> +		if (!wakeref)
> +			return -EFAULT;
>   	} else {
>   		/* No backing pages, no fallback, we must force GGTT access */
>   		wakeref = intel_runtime_pm_get(rpm);
> @@ -569,8 +577,6 @@ i915_gem_gtt_pwrite_fast(struct drm_i915_gem_object *obj,
>   		GEM_BUG_ON(!drm_mm_node_allocated(&node));
>   	}
>   
> -	mutex_unlock(&i915->drm.struct_mutex);
> -
>   	ret = i915_gem_object_lock_interruptible(obj);
>   	if (ret)
>   		goto out_unpin;
> @@ -634,18 +640,15 @@ i915_gem_gtt_pwrite_fast(struct drm_i915_gem_object *obj,
>   
>   	i915_gem_object_unlock_fence(obj, fence);
>   out_unpin:
> -	mutex_lock(&i915->drm.struct_mutex);
>   	intel_gt_flush_ggtt_writes(ggtt->vm.gt);
>   	if (drm_mm_node_allocated(&node)) {
>   		ggtt->vm.clear_range(&ggtt->vm, node.start, node.size);
> -		remove_mappable_node(&node);
> +		remove_mappable_node(ggtt, &node);
>   	} else {
>   		i915_vma_unpin(vma);
>   	}
>   out_rpm:
>   	intel_runtime_pm_put(rpm, wakeref);
> -out_unlock:
> -	mutex_unlock(&i915->drm.struct_mutex);
>   	return ret;
>   }
>   
> @@ -967,8 +970,6 @@ i915_gem_object_ggtt_pin(struct drm_i915_gem_object *obj,
>   	struct i915_vma *vma;
>   	int ret;
>   
> -	lockdep_assert_held(&obj->base.dev->struct_mutex);
> -
>   	if (flags & PIN_MAPPABLE &&
>   	    (!view || view->type == I915_GGTT_VIEW_NORMAL)) {
>   		/* If the required space is larger than the available
> @@ -1015,14 +1016,9 @@ i915_gem_object_ggtt_pin(struct drm_i915_gem_object *obj,
>   				return ERR_PTR(-ENOSPC);
>   		}
>   
> -		WARN(i915_vma_is_pinned(vma),
> -		     "bo is already pinned in ggtt with incorrect alignment:"
> -		     " offset=%08x, req.alignment=%llx,"
> -		     " req.map_and_fenceable=%d, vma->map_and_fenceable=%d\n",
> -		     i915_ggtt_offset(vma), alignment,
> -		     !!(flags & PIN_MAPPABLE),
> -		     i915_vma_is_map_and_fenceable(vma));
> +		mutex_lock(&vma->vm->mutex);
>   		ret = i915_vma_unbind(vma);
> +		mutex_unlock(&vma->vm->mutex);
>   		if (ret)
>   			return ERR_PTR(ret);
>   	}
> @@ -1328,7 +1324,9 @@ static int __intel_engines_record_defaults(struct drm_i915_private *i915)
>   		 * from the GTT to prevent such accidents and reclaim the
>   		 * space.
>   		 */
> +		mutex_lock(&state->vm->mutex);
>   		err = i915_vma_unbind(state);
> +		mutex_unlock(&state->vm->mutex);
>   		if (err)
>   			goto out;
>   
> diff --git a/drivers/gpu/drm/i915/i915_gem_evict.c b/drivers/gpu/drm/i915/i915_gem_evict.c
> index 7abcac3b5e2e..44f5b638fa43 100644
> --- a/drivers/gpu/drm/i915/i915_gem_evict.c
> +++ b/drivers/gpu/drm/i915/i915_gem_evict.c
> @@ -47,8 +47,7 @@ static int ggtt_flush(struct drm_i915_private *i915)
>   	 * bound by their active reference.
>   	 */
>   	return i915_gem_wait_for_idle(i915,
> -				      I915_WAIT_INTERRUPTIBLE |
> -				      I915_WAIT_LOCKED,
> +				      I915_WAIT_INTERRUPTIBLE,
>   				      MAX_SCHEDULE_TIMEOUT);
>   }
>   
> @@ -104,7 +103,7 @@ i915_gem_evict_something(struct i915_address_space *vm,
>   	struct i915_vma *active;
>   	int ret;
>   
> -	lockdep_assert_held(&vm->i915->drm.struct_mutex);
> +	lockdep_assert_held(&vm->mutex);
>   	trace_i915_gem_evict(vm, min_size, alignment, flags);
>   
>   	/*
> @@ -127,15 +126,6 @@ i915_gem_evict_something(struct i915_address_space *vm,
>   				    min_size, alignment, cache_level,
>   				    start, end, mode);
>   
> -	/*
> -	 * Retire before we search the active list. Although we have
> -	 * reasonable accuracy in our retirement lists, we may have
> -	 * a stray pin (preventing eviction) that can only be resolved by
> -	 * retiring.
> -	 */
> -	if (!(flags & PIN_NONBLOCK))
> -		i915_retire_requests(dev_priv);
> -
>   search_again:
>   	active = NULL;
>   	INIT_LIST_HEAD(&eviction_list);
> @@ -269,7 +259,7 @@ int i915_gem_evict_for_node(struct i915_address_space *vm,
>   	bool check_color;
>   	int ret = 0;
>   
> -	lockdep_assert_held(&vm->i915->drm.struct_mutex);
> +	lockdep_assert_held(&vm->mutex);
>   	GEM_BUG_ON(!IS_ALIGNED(start, I915_GTT_PAGE_SIZE));
>   	GEM_BUG_ON(!IS_ALIGNED(end, I915_GTT_PAGE_SIZE));
>   
> @@ -375,7 +365,7 @@ int i915_gem_evict_vm(struct i915_address_space *vm)
>   	struct i915_vma *vma, *next;
>   	int ret;
>   
> -	lockdep_assert_held(&vm->i915->drm.struct_mutex);
> +	lockdep_assert_held(&vm->mutex);
>   	trace_i915_gem_evict_vm(vm);
>   
>   	/* Switch back to the default context in order to unpin
> @@ -390,7 +380,6 @@ int i915_gem_evict_vm(struct i915_address_space *vm)
>   	}
>   
>   	INIT_LIST_HEAD(&eviction_list);
> -	mutex_lock(&vm->mutex);
>   	list_for_each_entry(vma, &vm->bound_list, vm_link) {
>   		if (i915_vma_is_pinned(vma))
>   			continue;
> @@ -398,7 +387,6 @@ int i915_gem_evict_vm(struct i915_address_space *vm)
>   		__i915_vma_pin(vma);
>   		list_add(&vma->evict_link, &eviction_list);
>   	}
> -	mutex_unlock(&vm->mutex);
>   
>   	ret = 0;
>   	list_for_each_entry_safe(vma, next, &eviction_list, evict_link) {
> diff --git a/drivers/gpu/drm/i915/i915_gem_fence_reg.c b/drivers/gpu/drm/i915/i915_gem_fence_reg.c
> index 615a9f4ef30c..414d839668d7 100644
> --- a/drivers/gpu/drm/i915/i915_gem_fence_reg.c
> +++ b/drivers/gpu/drm/i915/i915_gem_fence_reg.c
> @@ -237,6 +237,7 @@ static int fence_update(struct i915_fence_reg *fence,
>   
>   	old = xchg(&fence->vma, NULL);
>   	if (old) {
> +		/* XXX Ideally we would move the waiting to outside the mutex */
>   		ret = i915_active_wait(&old->active);
>   		if (ret) {
>   			fence->vma = old;
> @@ -331,13 +332,15 @@ static struct i915_fence_reg *fence_find(struct drm_i915_private *i915)
>   	return ERR_PTR(-EDEADLK);
>   }
>   
> -static int __i915_vma_pin_fence(struct i915_vma *vma)
> +int __i915_vma_pin_fence(struct i915_vma *vma)
>   {
>   	struct i915_ggtt *ggtt = i915_vm_to_ggtt(vma->vm);
>   	struct i915_fence_reg *fence;
>   	struct i915_vma *set = i915_gem_object_is_tiled(vma->obj) ? vma : NULL;
>   	int err;
>   
> +	lockdep_assert_held(&vma->vm->mutex);
> +
>   	/* Just update our place in the LRU if our fence is getting reused. */
>   	if (vma->fence) {
>   		fence = vma->fence;
> diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
> index 56d27cf09a3d..b2e4827788fc 100644
> --- a/drivers/gpu/drm/i915/i915_gem_gtt.c
> +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
> @@ -150,16 +150,18 @@ static void gmch_ggtt_invalidate(struct i915_ggtt *ggtt)
>   
>   static int ppgtt_bind_vma(struct i915_vma *vma,
>   			  enum i915_cache_level cache_level,
> -			  u32 unused)
> +			  u32 flags)
>   {
>   	u32 pte_flags;
>   	int err;
>   
> -	if (!i915_vma_is_bound(vma, I915_VMA_LOCAL_BIND)) {
> +	if (flags & I915_VMA_ALLOC) {
>   		err = vma->vm->allocate_va_range(vma->vm,
>   						 vma->node.start, vma->size);
>   		if (err)
>   			return err;
> +
> +		set_bit(I915_VMA_ALLOC_BIT, __i915_vma_flags(vma));
>   	}
>   
>   	/* Applicable to VLV, and gen8+ */
> @@ -167,6 +169,7 @@ static int ppgtt_bind_vma(struct i915_vma *vma,
>   	if (i915_gem_object_is_readonly(vma->obj))
>   		pte_flags |= PTE_READ_ONLY;
>   
> +	GEM_BUG_ON(!test_bit(I915_VMA_ALLOC_BIT, __i915_vma_flags(vma)));
>   	vma->vm->insert_entries(vma->vm, vma, cache_level, pte_flags);
>   	wmb();
>   
> @@ -175,7 +178,8 @@ static int ppgtt_bind_vma(struct i915_vma *vma,
>   
>   static void ppgtt_unbind_vma(struct i915_vma *vma)
>   {
> -	vma->vm->clear_range(vma->vm, vma->node.start, vma->size);
> +	if (test_and_clear_bit(I915_VMA_ALLOC_BIT, __i915_vma_flags(vma)))
> +		vma->vm->clear_range(vma->vm, vma->node.start, vma->size);
>   }
>   
>   static int ppgtt_set_pages(struct i915_vma *vma)
> @@ -503,15 +507,25 @@ static void i915_address_space_fini(struct i915_address_space *vm)
>   	mutex_destroy(&vm->mutex);
>   }
>   
> -static void ppgtt_destroy_vma(struct i915_address_space *vm)
> +void __i915_vm_close(struct i915_address_space *vm)
>   {
>   	struct i915_vma *vma, *vn;
>   
> -	mutex_lock(&vm->i915->drm.struct_mutex);
> -	list_for_each_entry_safe(vma, vn, &vm->bound_list, vm_link)
> +	mutex_lock(&vm->mutex);
> +	list_for_each_entry_safe(vma, vn, &vm->bound_list, vm_link) {
> +		struct drm_i915_gem_object *obj = vma->obj;
> +
> +		if (!kref_get_unless_zero(&obj->base.refcount))
> +			continue;
> +
> +		atomic_and(~I915_VMA_PIN_MASK, &vma->flags);
> +		WARN_ON(i915_vma_unbind(vma));
>   		i915_vma_destroy(vma);
> +
> +		i915_gem_object_put(obj);
> +	}
>   	GEM_BUG_ON(!list_empty(&vm->bound_list));
> -	mutex_unlock(&vm->i915->drm.struct_mutex);
> +	mutex_unlock(&vm->mutex);
>   }
>   
>   static void __i915_vm_release(struct work_struct *work)
> @@ -519,8 +533,6 @@ static void __i915_vm_release(struct work_struct *work)
>   	struct i915_address_space *vm =
>   		container_of(work, struct i915_address_space, rcu.work);
>   
> -	ppgtt_destroy_vma(vm);
> -
>   	vm->cleanup(vm);
>   	i915_address_space_fini(vm);
>   
> @@ -535,7 +547,6 @@ void i915_vm_release(struct kref *kref)
>   	GEM_BUG_ON(i915_is_ggtt(vm));
>   	trace_i915_ppgtt_release(vm);
>   
> -	vm->closed = true;
>   	queue_rcu_work(vm->i915->wq, &vm->rcu);
>   }
>   
> @@ -543,6 +554,7 @@ static 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);
>   
>   	/*
>   	 * The vm->mutex must be reclaim safe (for use in the shrinker).
> @@ -1769,12 +1781,8 @@ static void gen6_ppgtt_free_pd(struct gen6_ppgtt *ppgtt)
>   static void gen6_ppgtt_cleanup(struct i915_address_space *vm)
>   {
>   	struct gen6_ppgtt *ppgtt = to_gen6_ppgtt(i915_vm_to_ppgtt(vm));
> -	struct drm_i915_private *i915 = vm->i915;
>   
> -	/* FIXME remove the struct_mutex to bring the locking under control */
> -	mutex_lock(&i915->drm.struct_mutex);
>   	i915_vma_destroy(ppgtt->vma);
> -	mutex_unlock(&i915->drm.struct_mutex);
>   
>   	gen6_ppgtt_free_pd(ppgtt);
>   	free_scratch(vm);
> @@ -1863,7 +1871,8 @@ static struct i915_vma *pd_vma_create(struct gen6_ppgtt *ppgtt, int size)
>   
>   	i915_active_init(i915, &vma->active, NULL, NULL);
>   
> -	vma->vm = &ggtt->vm;
> +	mutex_init(&vma->pages_mutex);
> +	vma->vm = i915_vm_get(&ggtt->vm);
>   	vma->ops = &pd_vma_ops;
>   	vma->private = ppgtt;
>   
> @@ -1883,7 +1892,7 @@ int gen6_ppgtt_pin(struct i915_ppgtt *base)
>   	struct gen6_ppgtt *ppgtt = to_gen6_ppgtt(base);
>   	int err = 0;
>   
> -	GEM_BUG_ON(ppgtt->base.vm.closed);
> +	GEM_BUG_ON(!atomic_read(&ppgtt->base.vm.open));
>   
>   	/*
>   	 * Workaround the limited maximum vma->pin_count and the aliasing_ppgtt
> @@ -2460,14 +2469,18 @@ static int aliasing_gtt_bind_vma(struct i915_vma *vma,
>   	if (flags & I915_VMA_LOCAL_BIND) {
>   		struct i915_ppgtt *alias = i915_vm_to_ggtt(vma->vm)->alias;
>   
> -		if (!i915_vma_is_bound(vma, I915_VMA_LOCAL_BIND)) {
> +		if (flags & I915_VMA_ALLOC) {
>   			ret = alias->vm.allocate_va_range(&alias->vm,
>   							  vma->node.start,
>   							  vma->size);
>   			if (ret)
>   				return ret;
> +
> +			set_bit(I915_VMA_ALLOC_BIT, __i915_vma_flags(vma));
>   		}
>   
> +		GEM_BUG_ON(!test_bit(I915_VMA_ALLOC_BIT,
> +				     __i915_vma_flags(vma)));
>   		alias->vm.insert_entries(&alias->vm, vma,
>   					 cache_level, pte_flags);
>   	}
> @@ -2496,7 +2509,7 @@ static void aliasing_gtt_unbind_vma(struct i915_vma *vma)
>   			vm->clear_range(vm, vma->node.start, vma->size);
>   	}
>   
> -	if (i915_vma_is_bound(vma, I915_VMA_LOCAL_BIND)) {
> +	if (test_and_clear_bit(I915_VMA_ALLOC_BIT, __i915_vma_flags(vma))) {
>   		struct i915_address_space *vm =
>   			&i915_vm_to_ggtt(vma->vm)->alias->vm;
>   
> @@ -2599,22 +2612,16 @@ static int init_aliasing_ppgtt(struct i915_ggtt *ggtt)
>   
>   static void fini_aliasing_ppgtt(struct i915_ggtt *ggtt)
>   {
> -	struct drm_i915_private *i915 = ggtt->vm.i915;
>   	struct i915_ppgtt *ppgtt;
>   
> -	mutex_lock(&i915->drm.struct_mutex);
> -
>   	ppgtt = fetch_and_zero(&ggtt->alias);
>   	if (!ppgtt)
> -		goto out;
> +		return;
>   
>   	i915_vm_put(&ppgtt->vm);
>   
>   	ggtt->vm.vma_ops.bind_vma   = ggtt_bind_vma;
>   	ggtt->vm.vma_ops.unbind_vma = ggtt_unbind_vma;
> -
> -out:
> -	mutex_unlock(&i915->drm.struct_mutex);
>   }
>   
>   static int ggtt_reserve_guc_top(struct i915_ggtt *ggtt)
> @@ -2731,15 +2738,14 @@ int i915_init_ggtt(struct drm_i915_private *i915)
>   
>   static void ggtt_cleanup_hw(struct i915_ggtt *ggtt)
>   {
> -	struct drm_i915_private *i915 = ggtt->vm.i915;
>   	struct i915_vma *vma, *vn;
>   
> -	ggtt->vm.closed = true;
> +	atomic_set(&ggtt->vm.open, 0);
>   
>   	rcu_barrier(); /* flush the RCU'ed__i915_vm_release */
> -	flush_workqueue(i915->wq);
> +	flush_workqueue(ggtt->vm.i915->wq);
>   
> -	mutex_lock(&i915->drm.struct_mutex);
> +	mutex_lock(&ggtt->vm.mutex);
>   
>   	list_for_each_entry_safe(vma, vn, &ggtt->vm.bound_list, vm_link)
>   		WARN_ON(i915_vma_unbind(vma));
> @@ -2748,15 +2754,12 @@ static void ggtt_cleanup_hw(struct i915_ggtt *ggtt)
>   		drm_mm_remove_node(&ggtt->error_capture);
>   
>   	ggtt_release_guc_top(ggtt);
> -
> -	if (drm_mm_initialized(&ggtt->vm.mm)) {
> -		intel_vgt_deballoon(ggtt);
> -		i915_address_space_fini(&ggtt->vm);
> -	}
> +	intel_vgt_deballoon(ggtt);
>   
>   	ggtt->vm.cleanup(&ggtt->vm);
>   
> -	mutex_unlock(&i915->drm.struct_mutex);
> +	mutex_unlock(&ggtt->vm.mutex);
> +	i915_address_space_fini(&ggtt->vm);
>   
>   	arch_phys_wc_del(ggtt->mtrr);
>   	io_mapping_fini(&ggtt->iomap);
> @@ -3185,9 +3188,6 @@ int i915_ggtt_probe_hw(struct drm_i915_private *i915)
>   static int ggtt_init_hw(struct i915_ggtt *ggtt)
>   {
>   	struct drm_i915_private *i915 = ggtt->vm.i915;
> -	int ret = 0;
> -
> -	mutex_lock(&i915->drm.struct_mutex);
>   
>   	i915_address_space_init(&ggtt->vm, VM_CLASS_GGTT);
>   
> @@ -3203,18 +3203,14 @@ static int ggtt_init_hw(struct i915_ggtt *ggtt)
>   				ggtt->gmadr.start,
>   				ggtt->mappable_end)) {
>   		ggtt->vm.cleanup(&ggtt->vm);
> -		ret = -EIO;
> -		goto out;
> +		return -EIO;
>   	}
>   
>   	ggtt->mtrr = arch_phys_wc_add(ggtt->gmadr.start, ggtt->mappable_end);
>   
>   	i915_ggtt_init_fences(ggtt);
>   
> -out:
> -	mutex_unlock(&i915->drm.struct_mutex);
> -
> -	return ret;
> +	return 0;
>   }
>   
>   /**
> @@ -3286,6 +3282,7 @@ static void ggtt_restore_mappings(struct i915_ggtt *ggtt)
>   {
>   	struct i915_vma *vma, *vn;
>   	bool flush = false;
> +	int open;
>   
>   	intel_gt_check_and_clear_faults(ggtt->vm.gt);
>   
> @@ -3293,7 +3290,9 @@ static void ggtt_restore_mappings(struct i915_ggtt *ggtt)
>   
>   	/* First fill our portion of the GTT with scratch pages */
>   	ggtt->vm.clear_range(&ggtt->vm, 0, ggtt->vm.total);
> -	ggtt->vm.closed = true; /* skip rewriting PTE on VMA unbind */
> +
> +	/* Skip rewriting PTE on VMA unbind. */
> +	open = atomic_xchg(&ggtt->vm.open, 0);
>   
>   	/* clflush objects bound into the GGTT and rebind them. */
>   	list_for_each_entry_safe(vma, vn, &ggtt->vm.bound_list, vm_link) {
> @@ -3302,24 +3301,20 @@ static void ggtt_restore_mappings(struct i915_ggtt *ggtt)
>   		if (!i915_vma_is_bound(vma, I915_VMA_GLOBAL_BIND))
>   			continue;
>   
> -		mutex_unlock(&ggtt->vm.mutex);
> -
>   		if (!i915_vma_unbind(vma))
> -			goto lock;
> +			continue;
>   
> +		clear_bit(I915_VMA_GLOBAL_BIND_BIT, __i915_vma_flags(vma));
>   		WARN_ON(i915_vma_bind(vma,
>   				      obj ? obj->cache_level : 0,
> -				      PIN_UPDATE));
> +				      PIN_GLOBAL, NULL));
>   		if (obj) { /* only used during resume => exclusive access */
>   			flush |= fetch_and_zero(&obj->write_domain);
>   			obj->read_domains |= I915_GEM_DOMAIN_GTT;
>   		}
> -
> -lock:
> -		mutex_lock(&ggtt->vm.mutex);
>   	}
>   
> -	ggtt->vm.closed = false;
> +	atomic_set(&ggtt->vm.open, open);
>   	ggtt->invalidate(ggtt);
>   
>   	mutex_unlock(&ggtt->vm.mutex);
> @@ -3711,7 +3706,8 @@ int i915_gem_gtt_insert(struct i915_address_space *vm,
>   	u64 offset;
>   	int err;
>   
> -	lockdep_assert_held(&vm->i915->drm.struct_mutex);
> +	lockdep_assert_held(&vm->mutex);
> +
>   	GEM_BUG_ON(!size);
>   	GEM_BUG_ON(!IS_ALIGNED(size, I915_GTT_PAGE_SIZE));
>   	GEM_BUG_ON(alignment && !is_power_of_2(alignment));
> diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.h b/drivers/gpu/drm/i915/i915_gem_gtt.h
> index 007bdaf4ba00..e794d1f5eee8 100644
> --- a/drivers/gpu/drm/i915/i915_gem_gtt.h
> +++ b/drivers/gpu/drm/i915/i915_gem_gtt.h
> @@ -307,7 +307,7 @@ struct i915_address_space {
>   
>   	unsigned int bind_alloc;
>   
> -	bool closed;
> +	atomic_t open;
>   
>   	struct mutex mutex; /* protects vma and our lists */
>   #define VM_CLASS_GGTT 0
> @@ -575,6 +575,35 @@ static inline void i915_vm_put(struct i915_address_space *vm)
>   	kref_put(&vm->ref, i915_vm_release);
>   }
>   
> +static inline struct i915_address_space *
> +i915_vm_open(struct i915_address_space *vm)
> +{
> +	GEM_BUG_ON(!atomic_read(&vm->open));
> +	atomic_inc(&vm->open);
> +	return i915_vm_get(vm);
> +}
> +
> +static inline bool
> +i915_vm_tryopen(struct i915_address_space *vm)
> +{
> +	if (atomic_add_unless(&vm->open, 1, 0))
> +		return i915_vm_get(vm);
> +
> +	return false;
> +}
> +
> +void __i915_vm_close(struct i915_address_space *vm);
> +
> +static inline void
> +i915_vm_close(struct i915_address_space *vm)
> +{
> +	GEM_BUG_ON(!atomic_read(&vm->open));
> +	if (atomic_dec_and_test(&vm->open))
> +		__i915_vm_close(vm);
> +
> +	i915_vm_put(vm);
> +}
> +
>   int gen6_ppgtt_pin(struct i915_ppgtt *base);
>   void gen6_ppgtt_unpin(struct i915_ppgtt *base);
>   void gen6_ppgtt_unpin_all(struct i915_ppgtt *base);
> @@ -607,10 +636,9 @@ int i915_gem_gtt_insert(struct i915_address_space *vm,
>   #define PIN_OFFSET_BIAS		BIT_ULL(6)
>   #define PIN_OFFSET_FIXED	BIT_ULL(7)
>   
> -#define PIN_MBZ			BIT_ULL(8) /* I915_VMA_PIN_OVERFLOW */
> -#define PIN_GLOBAL		BIT_ULL(9) /* I915_VMA_GLOBAL_BIND */
> -#define PIN_USER		BIT_ULL(10) /* I915_VMA_LOCAL_BIND */
> -#define PIN_UPDATE		BIT_ULL(11)
> +#define PIN_UPDATE		BIT_ULL(9)
> +#define PIN_GLOBAL		BIT_ULL(10) /* I915_VMA_GLOBAL_BIND */
> +#define PIN_USER		BIT_ULL(11) /* I915_VMA_LOCAL_BIND */
>   
>   #define PIN_OFFSET_MASK		(-I915_GTT_PAGE_SIZE)
>   
> diff --git a/drivers/gpu/drm/i915/i915_perf.c b/drivers/gpu/drm/i915/i915_perf.c
> index c1b764233761..06633b4ad260 100644
> --- a/drivers/gpu/drm/i915/i915_perf.c
> +++ b/drivers/gpu/drm/i915/i915_perf.c
> @@ -1204,15 +1204,10 @@ static int i915_oa_read(struct i915_perf_stream *stream,
>   static struct intel_context *oa_pin_context(struct i915_perf_stream *stream)
>   {
>   	struct i915_gem_engines_iter it;
> -	struct drm_i915_private *i915 = stream->dev_priv;
>   	struct i915_gem_context *ctx = stream->ctx;
>   	struct intel_context *ce;
>   	int err;
>   
> -	err = i915_mutex_lock_interruptible(&i915->drm);
> -	if (err)
> -		return ERR_PTR(err);
> -
>   	for_each_gem_engine(ce, i915_gem_context_lock_engines(ctx), it) {
>   		if (ce->engine->class != RENDER_CLASS)
>   			continue;
> @@ -1229,10 +1224,6 @@ static struct intel_context *oa_pin_context(struct i915_perf_stream *stream)
>   	}
>   	i915_gem_context_unlock_engines(ctx);
>   
> -	mutex_unlock(&i915->drm.struct_mutex);
> -	if (err)
> -		return ERR_PTR(err);
> -
>   	return stream->pinned_ctx;
>   }
>   
> @@ -1331,32 +1322,22 @@ static int oa_get_render_ctx_id(struct i915_perf_stream *stream)
>    */
>   static void oa_put_render_ctx_id(struct i915_perf_stream *stream)
>   {
> -	struct drm_i915_private *dev_priv = stream->dev_priv;
>   	struct intel_context *ce;
>   
>   	stream->specific_ctx_id = INVALID_CTX_ID;
>   	stream->specific_ctx_id_mask = 0;
>   
>   	ce = fetch_and_zero(&stream->pinned_ctx);
> -	if (ce) {
> -		mutex_lock(&dev_priv->drm.struct_mutex);
> +	if (ce)
>   		intel_context_unpin(ce);
> -		mutex_unlock(&dev_priv->drm.struct_mutex);
> -	}
>   }
>   
>   static void
>   free_oa_buffer(struct i915_perf_stream *stream)
>   {
> -	struct drm_i915_private *i915 = stream->dev_priv;
> -
> -	mutex_lock(&i915->drm.struct_mutex);
> -
>   	i915_vma_unpin_and_release(&stream->oa_buffer.vma,
>   				   I915_VMA_RELEASE_MAP);
>   
> -	mutex_unlock(&i915->drm.struct_mutex);
> -
>   	stream->oa_buffer.vaddr = NULL;
>   }
>   
> @@ -1511,18 +1492,13 @@ static int alloc_oa_buffer(struct i915_perf_stream *stream)
>   	if (WARN_ON(stream->oa_buffer.vma))
>   		return -ENODEV;
>   
> -	ret = i915_mutex_lock_interruptible(&dev_priv->drm);
> -	if (ret)
> -		return ret;
> -
>   	BUILD_BUG_ON_NOT_POWER_OF_2(OA_BUFFER_SIZE);
>   	BUILD_BUG_ON(OA_BUFFER_SIZE < SZ_128K || OA_BUFFER_SIZE > SZ_16M);
>   
>   	bo = i915_gem_object_create_shmem(dev_priv, OA_BUFFER_SIZE);
>   	if (IS_ERR(bo)) {
>   		DRM_ERROR("Failed to allocate OA buffer\n");
> -		ret = PTR_ERR(bo);
> -		goto unlock;
> +		return PTR_ERR(bo);
>   	}
>   
>   	i915_gem_object_set_cache_coherency(bo, I915_CACHE_LLC);
> @@ -1546,7 +1522,7 @@ static int alloc_oa_buffer(struct i915_perf_stream *stream)
>   			 i915_ggtt_offset(stream->oa_buffer.vma),
>   			 stream->oa_buffer.vaddr);
>   
> -	goto unlock;
> +	return 0;
>   
>   err_unpin:
>   	__i915_vma_unpin(vma);
> @@ -1557,8 +1533,6 @@ static int alloc_oa_buffer(struct i915_perf_stream *stream)
>   	stream->oa_buffer.vaddr = NULL;
>   	stream->oa_buffer.vma = NULL;
>   
> -unlock:
> -	mutex_unlock(&dev_priv->drm.struct_mutex);
>   	return ret;
>   }
>   
> diff --git a/drivers/gpu/drm/i915/i915_vma.c b/drivers/gpu/drm/i915/i915_vma.c
> index 9adb85ba6daa..288679d8a6a9 100644
> --- a/drivers/gpu/drm/i915/i915_vma.c
> +++ b/drivers/gpu/drm/i915/i915_vma.c
> @@ -32,6 +32,7 @@
>   
>   #include "i915_drv.h"
>   #include "i915_globals.h"
> +#include "i915_sw_fence_work.h"
>   #include "i915_trace.h"
>   #include "i915_vma.h"
>   
> @@ -110,7 +111,8 @@ vma_create(struct drm_i915_gem_object *obj,
>   	if (vma == NULL)
>   		return ERR_PTR(-ENOMEM);
>   
> -	vma->vm = vm;
> +	mutex_init(&vma->pages_mutex);
> +	vma->vm = i915_vm_get(vm);
>   	vma->ops = &vm->vma_ops;
>   	vma->obj = obj;
>   	vma->resv = obj->base.resv;
> @@ -261,8 +263,6 @@ vma_lookup(struct drm_i915_gem_object *obj,
>    * Once created, the VMA is kept until either the object is freed, or the
>    * address space is closed.
>    *
> - * Must be called with struct_mutex held.
> - *
>    * Returns the vma, or an error pointer.
>    */
>   struct i915_vma *
> @@ -273,7 +273,7 @@ i915_vma_instance(struct drm_i915_gem_object *obj,
>   	struct i915_vma *vma;
>   
>   	GEM_BUG_ON(view && !i915_is_ggtt(vm));
> -	GEM_BUG_ON(vm->closed);
> +	GEM_BUG_ON(!atomic_read(&vm->open));
>   
>   	spin_lock(&obj->vma.lock);
>   	vma = vma_lookup(obj, vm, view);
> @@ -287,18 +287,63 @@ i915_vma_instance(struct drm_i915_gem_object *obj,
>   	return vma;
>   }
>   
> +struct i915_vma_work {
> +	struct dma_fence_work base;
> +	struct i915_vma *vma;
> +	enum i915_cache_level cache_level;
> +	unsigned int flags;
> +};
> +
> +static int __vma_bind(struct dma_fence_work *work)
> +{
> +	struct i915_vma_work *vw = container_of(work, typeof(*vw), base);
> +	struct i915_vma *vma = vw->vma;
> +	int err;
> +
> +	err = vma->ops->bind_vma(vma, vw->cache_level, vw->flags);
> +	if (err)
> +		atomic_or(I915_VMA_ERROR, &vma->flags);
> +
> +	if (vma->obj)
> +		__i915_gem_object_unpin_pages(vma->obj);
> +
> +	return err;
> +}
> +
> +static const struct dma_fence_work_ops bind_ops = {
> +	.name = "bind",
> +	.work = __vma_bind,
> +};
> +
> +struct i915_vma_work *i915_vma_work(void)
> +{
> +	struct i915_vma_work *vw;
> +
> +	vw = kzalloc(sizeof(*vw), GFP_KERNEL);
> +	if (!vw)
> +		return NULL;
> +
> +	dma_fence_work_init(&vw->base, &bind_ops);
> +	vw->base.dma.error = -EAGAIN; /* disable the worker by default */
> +
> +	return vw;
> +}
> +
>   /**
>    * i915_vma_bind - Sets up PTEs for an VMA in it's corresponding address space.
>    * @vma: VMA to map
>    * @cache_level: mapping cache level
>    * @flags: flags like global or local mapping
> + * @work: preallocated worker for allocating and binding the PTE
>    *
>    * DMA addresses are taken from the scatter-gather table of this object (or of
>    * this VMA in case of non-default GGTT views) and PTE entries set up.
>    * Note that DMA addresses are also the only part of the SG table we care about.
>    */
> -int i915_vma_bind(struct i915_vma *vma, enum i915_cache_level cache_level,
> -		  u32 flags)
> +int i915_vma_bind(struct i915_vma *vma,
> +		  enum i915_cache_level cache_level,
> +		  u32 flags,
> +		  struct i915_vma_work *work)
>   {
>   	u32 bind_flags;
>   	u32 vma_flags;
> @@ -315,11 +360,8 @@ int i915_vma_bind(struct i915_vma *vma, enum i915_cache_level cache_level,
>   	if (GEM_DEBUG_WARN_ON(!flags))
>   		return -EINVAL;
>   
> -	bind_flags = 0;
> -	if (flags & PIN_GLOBAL)
> -		bind_flags |= I915_VMA_GLOBAL_BIND;
> -	if (flags & PIN_USER)
> -		bind_flags |= I915_VMA_LOCAL_BIND;
> +	bind_flags = flags;
> +	bind_flags &= I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND;
>   
>   	vma_flags = atomic_read(&vma->flags);
>   	vma_flags &= I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND;
> @@ -333,9 +375,32 @@ int i915_vma_bind(struct i915_vma *vma, enum i915_cache_level cache_level,
>   	GEM_BUG_ON(!vma->pages);
>   
>   	trace_i915_vma_bind(vma, bind_flags);
> -	ret = vma->ops->bind_vma(vma, cache_level, bind_flags);
> -	if (ret)
> -		return ret;
> +	if (work && (bind_flags & ~vma_flags) & vma->vm->bind_alloc) {
> +		work->vma = vma;
> +		work->cache_level = cache_level;
> +		work->flags = bind_flags | I915_VMA_ALLOC;
> +
> +		/*
> +		 * Note we only want to chain up to the migration fence on
> +		 * the pages (not the object itself). As we don't track that,
> +		 * yet, we have to use the exclusive fence instead.
> +		 *
> +		 * Also note that we do not want to track the async vma as
> +		 * part of the obj->resv->excl_fence as it only affects
> +		 * execution and not content or object's backing store lifetime.
> +		 */
> +		GEM_BUG_ON(i915_active_has_exclusive(&vma->active));
> +		i915_active_set_exclusive(&vma->active, &work->base.dma);
> +		work->base.dma.error = 0; /* enable the queue_work() */
> +
> +		if (vma->obj)
> +			__i915_gem_object_pin_pages(vma->obj);
> +	} else {
> +		GEM_BUG_ON((bind_flags & ~vma_flags) & vma->vm->bind_alloc);
> +		ret = vma->ops->bind_vma(vma, cache_level, bind_flags);
> +		if (ret)
> +			return ret;
> +	}
>   
>   	atomic_or(bind_flags, &vma->flags);
>   	return 0;
> @@ -348,9 +413,7 @@ void __iomem *i915_vma_pin_iomap(struct i915_vma *vma)
>   
>   	/* Access through the GTT requires the device to be awake. */
>   	assert_rpm_wakelock_held(&vma->vm->i915->runtime_pm);
> -
> -	lockdep_assert_held(&vma->vm->i915->drm.struct_mutex);
> -	if (WARN_ON(!i915_vma_is_map_and_fenceable(vma))) {
> +	if (GEM_WARN_ON(!i915_vma_is_map_and_fenceable(vma))) {
>   		err = -ENODEV;
>   		goto err;
>   	}
> @@ -368,7 +431,8 @@ void __iomem *i915_vma_pin_iomap(struct i915_vma *vma)
>   			goto err;
>   		}
>   
> -		vma->iomap = ptr;
> +		if (unlikely(cmpxchg(&vma->iomap, NULL, ptr)))
> +			io_mapping_unmap(ptr);
>   	}
>   
>   	__i915_vma_pin(vma);
> @@ -388,18 +452,12 @@ void __iomem *i915_vma_pin_iomap(struct i915_vma *vma)
>   
>   void i915_vma_flush_writes(struct i915_vma *vma)
>   {
> -	if (!i915_vma_has_ggtt_write(vma))
> -		return;
> -
> -	intel_gt_flush_ggtt_writes(vma->vm->gt);
> -
> -	i915_vma_unset_ggtt_write(vma);
> +	if (i915_vma_unset_ggtt_write(vma))
> +		intel_gt_flush_ggtt_writes(vma->vm->gt);
>   }
>   
>   void i915_vma_unpin_iomap(struct i915_vma *vma)
>   {
> -	lockdep_assert_held(&vma->vm->i915->drm.struct_mutex);
> -
>   	GEM_BUG_ON(vma->iomap == NULL);
>   
>   	i915_vma_flush_writes(vma);
> @@ -435,6 +493,9 @@ bool i915_vma_misplaced(const struct i915_vma *vma,
>   	if (!drm_mm_node_allocated(&vma->node))
>   		return false;
>   
> +	if (test_bit(I915_VMA_ERROR_BIT, __i915_vma_flags(vma)))
> +		return true;
> +
>   	if (vma->node.size < size)
>   		return true;
>   
> @@ -538,7 +599,6 @@ static void assert_bind_count(const struct drm_i915_gem_object *obj)
>   static int
>   i915_vma_insert(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
>   {
> -	struct drm_i915_private *dev_priv = vma->vm->i915;
>   	unsigned int cache_level;
>   	u64 start, end;
>   	int ret;
> @@ -564,7 +624,7 @@ i915_vma_insert(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
>   
>   	end = vma->vm->total;
>   	if (flags & PIN_MAPPABLE)
> -		end = min_t(u64, end, dev_priv->ggtt.mappable_end);
> +		end = min_t(u64, end, i915_vm_to_ggtt(vma->vm)->mappable_end);
>   	if (flags & PIN_ZONE_4G)
>   		end = min_t(u64, end, (1ULL << 32) - I915_GTT_PAGE_SIZE);
>   	GEM_BUG_ON(!IS_ALIGNED(end, I915_GTT_PAGE_SIZE));
> @@ -580,35 +640,21 @@ i915_vma_insert(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
>   		return -ENOSPC;
>   	}
>   
> -	if (vma->obj) {
> -		ret = i915_gem_object_pin_pages(vma->obj);
> -		if (ret)
> -			return ret;
> -
> +	cache_level = 0;
> +	if (vma->obj)
>   		cache_level = vma->obj->cache_level;
> -	} else {
> -		cache_level = 0;
> -	}
> -
> -	GEM_BUG_ON(vma->pages);
> -
> -	ret = vma->ops->set_pages(vma);
> -	if (ret)
> -		goto err_unpin;
>   
>   	if (flags & PIN_OFFSET_FIXED) {
>   		u64 offset = flags & PIN_OFFSET_MASK;
>   		if (!IS_ALIGNED(offset, alignment) ||
> -		    range_overflows(offset, size, end)) {
> -			ret = -EINVAL;
> -			goto err_clear;
> -		}
> +		    range_overflows(offset, size, end))
> +			return -EINVAL;
>   
>   		ret = i915_gem_gtt_reserve(vma->vm, &vma->node,
>   					   size, offset, cache_level,
>   					   flags);
>   		if (ret)
> -			goto err_clear;
> +			return ret;
>   	} else {
>   		/*
>   		 * We only support huge gtt pages through the 48b PPGTT,
> @@ -647,7 +693,7 @@ i915_vma_insert(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
>   					  size, alignment, cache_level,
>   					  start, end, flags);
>   		if (ret)
> -			goto err_clear;
> +			return ret;
>   
>   		GEM_BUG_ON(vma->node.start < start);
>   		GEM_BUG_ON(vma->node.start + vma->node.size > end);
> @@ -655,23 +701,15 @@ 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, cache_level));
>   
> -	mutex_lock(&vma->vm->mutex);
>   	list_add_tail(&vma->vm_link, &vma->vm->bound_list);
> -	mutex_unlock(&vma->vm->mutex);
>   
>   	if (vma->obj) {
> +		atomic_inc(&vma->obj->mm.pages_pin_count);
>   		atomic_inc(&vma->obj->bind_count);
>   		assert_bind_count(vma->obj);
>   	}
>   
>   	return 0;
> -
> -err_clear:
> -	vma->ops->clear_pages(vma);
> -err_unpin:
> -	if (vma->obj)
> -		i915_gem_object_unpin_pages(vma->obj);
> -	return ret;
>   }
>   
>   static void
> @@ -680,12 +718,7 @@ i915_vma_remove(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));
>   
> -	vma->ops->clear_pages(vma);
> -
> -	mutex_lock(&vma->vm->mutex);
> -	drm_mm_remove_node(&vma->node);
>   	list_del(&vma->vm_link);
> -	mutex_unlock(&vma->vm->mutex);
>   
>   	/*
>   	 * Since the unbound list is global, only move to that list if
> @@ -704,51 +737,206 @@ i915_vma_remove(struct i915_vma *vma)
>   		i915_gem_object_unpin_pages(obj);
>   		assert_bind_count(obj);
>   	}
> +
> +	drm_mm_remove_node(&vma->node);
>   }
>   
> -int __i915_vma_do_pin(struct i915_vma *vma,
> -		      u64 size, u64 alignment, u64 flags)
> +static bool try_fast_pin(struct i915_vma *vma, unsigned int flags)
>   {
> -	const unsigned int bound = atomic_read(&vma->flags);
> -	int ret;
> +	unsigned int bound;
> +	bool pinned = true;
>   
> -	lockdep_assert_held(&vma->vm->i915->drm.struct_mutex);
> -	GEM_BUG_ON((flags & (PIN_GLOBAL | PIN_USER)) == 0);
> -	GEM_BUG_ON((flags & PIN_GLOBAL) && !i915_vma_is_ggtt(vma));
> +	bound = atomic_read(&vma->flags);
> +	do {
> +		if (unlikely(flags & ~bound))
> +			return false;
>   
> -	if (WARN_ON(bound & I915_VMA_PIN_OVERFLOW)) {
> -		ret = -EBUSY;
> -		goto err_unpin;
> +		if (unlikely(bound & (I915_VMA_OVERFLOW | I915_VMA_ERROR)))
> +			return false;
> +
> +		if (!(bound & I915_VMA_PIN_MASK))
> +			goto slow;
> +
> +		GEM_BUG_ON(((bound + 1) & I915_VMA_PIN_MASK) == 0);
> +	} while (!atomic_try_cmpxchg(&vma->flags, &bound, bound + 1));
> +
> +	return true;
> +
> +slow:
> +	/*
> +	 * If pin_count==0, but we are bound, check under the lock to avoid
> +	 * racing with a concurrent i915_vma_unbind().
> +	 */
> +	mutex_lock(&vma->vm->mutex);
> +	do {
> +		if (unlikely(bound & (I915_VMA_OVERFLOW | I915_VMA_ERROR))) {
> +			pinned = false;
> +			break;
> +		}
> +
> +		if (unlikely(flags & ~bound)) {
> +			pinned = false;
> +			break;
> +		}
> +	} while (!atomic_try_cmpxchg(&vma->flags, &bound, bound + 1));
> +	mutex_unlock(&vma->vm->mutex);
> +
> +	return pinned;
> +}
> +
> +static int vma_get_pages(struct i915_vma *vma)
> +{
> +	int err = 0;
> +
> +	if (atomic_add_unless(&vma->pages_count, 1, 0))
> +		return 0;
> +
> +	if (mutex_lock_interruptible(&vma->pages_mutex))
> +		return -EINTR;
> +
> +	if (!atomic_read(&vma->pages_count)) {
> +		if (vma->obj) {
> +			err = i915_gem_object_pin_pages(vma->obj);
> +			if (err)
> +				goto unlock;
> +		}
> +
> +		err = vma->ops->set_pages(vma);
> +		if (err)
> +			goto unlock;
>   	}
> +	atomic_inc(&vma->pages_count);
>   
> -	if ((bound & I915_VMA_BIND_MASK) == 0) {
> -		ret = i915_vma_insert(vma, size, alignment, flags);
> -		if (ret)
> -			goto err_unpin;
> +unlock:
> +	mutex_unlock(&vma->pages_mutex);
> +
> +	return err;
> +}
> +
> +static void __vma_put_pages(struct i915_vma *vma, unsigned int count)
> +{
> +	mutex_lock_nested(&vma->pages_mutex, SINGLE_DEPTH_NESTING);
> +	GEM_BUG_ON(atomic_read(&vma->pages_count) < count);
> +	if (atomic_sub_return(count, &vma->pages_count) == 0) {
> +		vma->ops->clear_pages(vma);
> +		GEM_BUG_ON(vma->pages);
> +		if (vma->obj)
> +			i915_gem_object_unpin_pages(vma->obj);
>   	}
> -	GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
> +	mutex_unlock(&vma->pages_mutex);
> +}
>   
> -	ret = i915_vma_bind(vma, vma->obj ? vma->obj->cache_level : 0, flags);
> -	if (ret)
> -		goto err_remove;
> +static void vma_put_pages(struct i915_vma *vma)
> +{
> +	if (atomic_add_unless(&vma->pages_count, -1, 1))
> +		return;
>   
> -	GEM_BUG_ON(!i915_vma_is_bound(vma, I915_VMA_BIND_MASK));
> +	__vma_put_pages(vma, 1);
> +}
> +
> +static void vma_unbind_pages(struct i915_vma *vma)
> +{
> +	unsigned int count;
> +
> +	lockdep_assert_held(&vma->vm->mutex);
> +
> +	count = atomic_read(&vma->pages_count);
> +	count >>= I915_VMA_PAGES_BIAS;
> +	GEM_BUG_ON(!count);
> +
> +	__vma_put_pages(vma, count | count << I915_VMA_PAGES_BIAS);
> +}
> +
> +int i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
> +{
> +	struct i915_vma_work *work = NULL;
> +	unsigned int bound;
> +	int err;
> +
> +	BUILD_BUG_ON(PIN_GLOBAL != I915_VMA_GLOBAL_BIND);
> +	BUILD_BUG_ON(PIN_USER != I915_VMA_LOCAL_BIND);
> +
> +	GEM_BUG_ON(flags & PIN_UPDATE);
> +	GEM_BUG_ON(!(flags & (PIN_USER | PIN_GLOBAL)));
>   
> -	if ((bound ^ atomic_read(&vma->flags)) & I915_VMA_GLOBAL_BIND)
> -		__i915_vma_set_map_and_fenceable(vma);
> +	if (try_fast_pin(vma, flags & I915_VMA_BIND_MASK))
> +		return 0;
> +
> +	err = vma_get_pages(vma);
> +	if (err)
> +		return err;
> +
> +	if (flags & PIN_USER) {
> +		work = i915_vma_work();
> +		if (!work) {
> +			err = -ENOMEM;
> +			goto err_pages;
> +		}
> +	}
> +
> +	err = mutex_lock_interruptible(&vma->vm->mutex);
> +	if (err)
> +		goto err_fence;
> +
> +	bound = atomic_read(&vma->flags);
> +	if (unlikely(bound & I915_VMA_ERROR)) {
> +		err = -ENOMEM;
> +		goto err_unlock;
> +	}
>   
> +	if (unlikely(!((bound + 1) & I915_VMA_PIN_MASK))) {
> +		err = -EAGAIN; /* pins are meant to be fairly temporary */
> +		goto err_unlock;
> +	}
> +
> +	if (unlikely(!(flags & ~bound & I915_VMA_BIND_MASK))) {
> +		__i915_vma_pin(vma);
> +		goto err_unlock;
> +	}
> +
> +	err = i915_active_acquire(&vma->active);
> +	if (err)
> +		goto err_unlock;
> +
> +	if (!(bound & I915_VMA_BIND_MASK)) {
> +		err = i915_vma_insert(vma, size, alignment, flags);
> +		if (err)
> +			goto err_active;
> +
> +		if (i915_is_ggtt(vma->vm))
> +			__i915_vma_set_map_and_fenceable(vma);
> +	}
> +
> +	GEM_BUG_ON(!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);
> +
> +	__i915_vma_pin(vma);
> +	GEM_BUG_ON(!i915_vma_is_pinned(vma));
> +	GEM_BUG_ON(!i915_vma_is_bound(vma, flags));
>   	GEM_BUG_ON(i915_vma_misplaced(vma, size, alignment, flags));
> -	return 0;
>   
>   err_remove:
> -	if ((bound & I915_VMA_BIND_MASK) == 0) {
> +	if (!i915_vma_is_bound(vma, I915_VMA_BIND_MASK))
>   		i915_vma_remove(vma);
> -		GEM_BUG_ON(vma->pages);
> -		GEM_BUG_ON(atomic_read(&vma->flags) & I915_VMA_BIND_MASK);
> -	}
> -err_unpin:
> -	__i915_vma_unpin(vma);
> -	return ret;
> +err_active:
> +	i915_active_release(&vma->active);
> +err_unlock:
> +	mutex_unlock(&vma->vm->mutex);
> +err_fence:
> +	if (work)
> +		dma_fence_work_commit(&work->base);
> +err_pages:
> +	vma_put_pages(vma);
> +	return err;
>   }
>   
>   void i915_vma_close(struct i915_vma *vma)
> @@ -779,9 +967,6 @@ static void __i915_vma_remove_closed(struct i915_vma *vma)
>   {
>   	struct drm_i915_private *i915 = vma->vm->i915;
>   
> -	if (!i915_vma_is_closed(vma))
> -		return;
> -
>   	spin_lock_irq(&i915->gt.closed_lock);
>   	list_del_init(&vma->closed_link);
>   	spin_unlock_irq(&i915->gt.closed_lock);
> @@ -789,40 +974,35 @@ static void __i915_vma_remove_closed(struct i915_vma *vma)
>   
>   void i915_vma_reopen(struct i915_vma *vma)
>   {
> -	__i915_vma_remove_closed(vma);
> +	if (i915_vma_is_closed(vma))
> +		__i915_vma_remove_closed(vma);
>   }
>   
> -static void __i915_vma_destroy(struct i915_vma *vma)
> +void i915_vma_destroy(struct i915_vma *vma)
>   {
> -	GEM_BUG_ON(drm_mm_node_allocated(&vma->node));
> -	GEM_BUG_ON(vma->fence);
> +	if (drm_mm_node_allocated(&vma->node)) {
> +		mutex_lock(&vma->vm->mutex);
> +		atomic_and(~I915_VMA_PIN_MASK, &vma->flags);
> +		WARN_ON(i915_vma_unbind(vma));
> +		mutex_unlock(&vma->vm->mutex);
> +		GEM_BUG_ON(drm_mm_node_allocated(&vma->node));
> +	}
> +	GEM_BUG_ON(i915_vma_is_active(vma));
>   
>   	if (vma->obj) {
>   		struct drm_i915_gem_object *obj = vma->obj;
>   
>   		spin_lock(&obj->vma.lock);
>   		list_del(&vma->obj_link);
> -		rb_erase(&vma->obj_node, &vma->obj->vma.tree);
> +		rb_erase(&vma->obj_node, &obj->vma.tree);
>   		spin_unlock(&obj->vma.lock);
>   	}
>   
> -	i915_active_fini(&vma->active);
> -
> -	i915_vma_free(vma);
> -}
> -
> -void i915_vma_destroy(struct i915_vma *vma)
> -{
> -	lockdep_assert_held(&vma->vm->i915->drm.struct_mutex);
> -
> -	GEM_BUG_ON(i915_vma_is_pinned(vma));
> -
>   	__i915_vma_remove_closed(vma);
> +	i915_vm_put(vma->vm);
>   
> -	WARN_ON(i915_vma_unbind(vma));
> -	GEM_BUG_ON(i915_vma_is_active(vma));
> -
> -	__i915_vma_destroy(vma);
> +	i915_active_fini(&vma->active);
> +	i915_vma_free(vma);
>   }
>   
>   void i915_vma_parked(struct drm_i915_private *i915)
> @@ -831,12 +1011,32 @@ void i915_vma_parked(struct drm_i915_private *i915)
>   
>   	spin_lock_irq(&i915->gt.closed_lock);
>   	list_for_each_entry_safe(vma, next, &i915->gt.closed_vma, closed_link) {
> -		list_del_init(&vma->closed_link);
> +		struct drm_i915_gem_object *obj = vma->obj;
> +		struct i915_address_space *vm = vma->vm;
> +
> +		/* XXX All to avoid keeping a reference on i915_vma itself */
> +
> +		if (!kref_get_unless_zero(&obj->base.refcount))
> +			continue;
> +
> +		if (!i915_vm_tryopen(vm)) {
> +			i915_gem_object_put(obj);
> +			obj = NULL;
> +		}
> +
>   		spin_unlock_irq(&i915->gt.closed_lock);
>   
> -		i915_vma_destroy(vma);
> +		if (obj) {
> +			i915_vma_destroy(vma);
> +			i915_gem_object_put(obj);
> +		}
> +
> +		i915_vm_close(vm);
>   
> +		/* Restart after dropping lock */
>   		spin_lock_irq(&i915->gt.closed_lock);
> +		next = list_first_entry(&i915->gt.closed_vma,
> +					typeof(*next), closed_link);
>   	}
>   	spin_unlock_irq(&i915->gt.closed_lock);
>   }
> @@ -887,6 +1087,10 @@ int i915_vma_move_to_active(struct i915_vma *vma,
>   	assert_object_held(obj);
>   	GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
>   
> +	err = i915_request_await_active(rq, &vma->active);
> +	if (err)
> +		return err;
> +
>   	/*
>   	 * Add a reference if we're newly entering the active list.
>   	 * The order in which we add operations to the retirement queue is
> @@ -927,34 +1131,19 @@ int i915_vma_unbind(struct i915_vma *vma)
>   {
>   	int ret;
>   
> -	lockdep_assert_held(&vma->vm->i915->drm.struct_mutex);
> +	lockdep_assert_held(&vma->vm->mutex);
>   
>   	/*
>   	 * First wait upon any activity as retiring the request may
>   	 * have side-effects such as unpinning or even unbinding this vma.
> +	 *
> +	 * XXX Actually waiting under the vm->mutex is a hinderance and
> +	 * should be pipelined wherever possible. In cases where that is
> +	 * unavoidable, we should lift the wait to before the mutex.
>   	 */
> -	might_sleep();
> -	if (i915_vma_is_active(vma)) {
> -		/*
> -		 * When a closed VMA is retired, it is unbound - eek.
> -		 * In order to prevent it from being recursively closed,
> -		 * take a pin on the vma so that the second unbind is
> -		 * aborted.
> -		 *
> -		 * Even more scary is that the retire callback may free
> -		 * the object (last active vma). To prevent the explosion
> -		 * we defer the actual object free to a worker that can
> -		 * only proceed once it acquires the struct_mutex (which
> -		 * we currently hold, therefore it cannot free this object
> -		 * before we are finished).
> -		 */
> -		__i915_vma_pin(vma);
> -		ret = i915_active_wait(&vma->active);
> -		__i915_vma_unpin(vma);
> -		if (ret)
> -			return ret;
> -	}
> -	GEM_BUG_ON(i915_vma_is_active(vma));
> +	ret = i915_active_wait(&vma->active);
> +	if (ret)
> +		return ret;
>   
>   	if (i915_vma_is_pinned(vma)) {
>   		vma_print_allocator(vma, "is pinned");
> @@ -975,16 +1164,12 @@ int i915_vma_unbind(struct i915_vma *vma)
>   		GEM_BUG_ON(i915_vma_has_ggtt_write(vma));
>   
>   		/* release the fence reg _after_ flushing */
> -		mutex_lock(&vma->vm->mutex);
>   		ret = i915_vma_revoke_fence(vma);
> -		mutex_unlock(&vma->vm->mutex);
>   		if (ret)
>   			return ret;
>   
>   		/* Force a pagefault for domain tracking on next user access */
> -		mutex_lock(&vma->vm->mutex);
>   		i915_vma_revoke_mmap(vma);
> -		mutex_unlock(&vma->vm->mutex);
>   
>   		__i915_vma_iounmap(vma);
>   		clear_bit(I915_VMA_CAN_FENCE_BIT, __i915_vma_flags(vma));
> @@ -992,12 +1177,13 @@ int i915_vma_unbind(struct i915_vma *vma)
>   	GEM_BUG_ON(vma->fence);
>   	GEM_BUG_ON(i915_vma_has_userfault(vma));
>   
> -	if (likely(!vma->vm->closed)) {
> +	if (likely(atomic_read(&vma->vm->open))) {
>   		trace_i915_vma_unbind(vma);
>   		vma->ops->unbind_vma(vma);
>   	}
> -	atomic_and(~I915_VMA_BIND_MASK, &vma->flags);
> +	atomic_and(~(I915_VMA_BIND_MASK | I915_VMA_ERROR), &vma->flags);
>   
> +	vma_unbind_pages(vma);
>   	i915_vma_remove(vma);
>   
>   	return 0;
> diff --git a/drivers/gpu/drm/i915/i915_vma.h b/drivers/gpu/drm/i915/i915_vma.h
> index 6053ce1dc95c..a784dc879168 100644
> --- a/drivers/gpu/drm/i915/i915_vma.h
> +++ b/drivers/gpu/drm/i915/i915_vma.h
> @@ -96,25 +96,28 @@ struct i915_vma {
>   	 * exclusive cachelines of a single page, so a maximum of 64 possible
>   	 * users.
>   	 */
> -#define I915_VMA_PIN_MASK 0xff
> -#define I915_VMA_PIN_OVERFLOW_BIT 8
> -#define I915_VMA_PIN_OVERFLOW	((int)BIT(I915_VMA_PIN_OVERFLOW_BIT))
> +#define I915_VMA_PIN_MASK 0x3ff
> +#define I915_VMA_OVERFLOW 0x200
>   
>   	/** Flags and address space this VMA is bound to */
> -#define I915_VMA_GLOBAL_BIND_BIT 9
> -#define I915_VMA_LOCAL_BIND_BIT 10
> +#define I915_VMA_GLOBAL_BIND_BIT 10
> +#define I915_VMA_LOCAL_BIND_BIT  11
>   
>   #define I915_VMA_GLOBAL_BIND	((int)BIT(I915_VMA_GLOBAL_BIND_BIT))
>   #define I915_VMA_LOCAL_BIND	((int)BIT(I915_VMA_LOCAL_BIND_BIT))
>   
> -#define I915_VMA_BIND_MASK (I915_VMA_GLOBAL_BIND | \
> -			    I915_VMA_LOCAL_BIND | \
> -			    I915_VMA_PIN_OVERFLOW)
> +#define I915_VMA_BIND_MASK (I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND)
>   
> -#define I915_VMA_GGTT_BIT	11
> -#define I915_VMA_CAN_FENCE_BIT	12
> -#define I915_VMA_USERFAULT_BIT	13
> -#define I915_VMA_GGTT_WRITE_BIT	14
> +#define I915_VMA_ALLOC_BIT	12
> +#define I915_VMA_ALLOC		((int)BIT(I915_VMA_ALLOC_BIT))
> +
> +#define I915_VMA_ERROR_BIT	13
> +#define I915_VMA_ERROR		((int)BIT(I915_VMA_ERROR_BIT))
> +
> +#define I915_VMA_GGTT_BIT	14
> +#define I915_VMA_CAN_FENCE_BIT	15
> +#define I915_VMA_USERFAULT_BIT	16
> +#define I915_VMA_GGTT_WRITE_BIT	17
>   
>   #define I915_VMA_GGTT		((int)BIT(I915_VMA_GGTT_BIT))
>   #define I915_VMA_CAN_FENCE	((int)BIT(I915_VMA_CAN_FENCE_BIT))
> @@ -123,6 +126,11 @@ 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.
> @@ -307,8 +315,12 @@ i915_vma_compare(struct i915_vma *vma,
>   	return memcmp(&vma->ggtt_view.partial, &view->partial, view->type);
>   }
>   
> -int i915_vma_bind(struct i915_vma *vma, enum i915_cache_level cache_level,
> -		  u32 flags);
> +struct i915_vma_work *i915_vma_work(void);
> +int i915_vma_bind(struct i915_vma *vma,
> +		  enum i915_cache_level cache_level,
> +		  u32 flags,
> +		  struct i915_vma_work *work);
> +
>   bool i915_gem_valid_gtt_space(struct i915_vma *vma, unsigned long cache_level);
>   bool i915_vma_misplaced(const struct i915_vma *vma,
>   			u64 size, u64 alignment, u64 flags);
> @@ -332,26 +344,8 @@ static inline void i915_vma_unlock(struct i915_vma *vma)
>   	dma_resv_unlock(vma->resv);
>   }
>   
> -int __i915_vma_do_pin(struct i915_vma *vma,
> -		      u64 size, u64 alignment, u64 flags);
> -static inline int __must_check
> -i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
> -{
> -	BUILD_BUG_ON(PIN_MBZ != I915_VMA_PIN_OVERFLOW);
> -	BUILD_BUG_ON(PIN_GLOBAL != I915_VMA_GLOBAL_BIND);
> -	BUILD_BUG_ON(PIN_USER != I915_VMA_LOCAL_BIND);
> -
> -	/* Pin early to prevent the shrinker/eviction logic from destroying
> -	 * our vma as we insert and bind.
> -	 */
> -	if (likely(((atomic_inc_return(&vma->flags) ^ flags) & I915_VMA_BIND_MASK) == 0)) {
> -		GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
> -		GEM_BUG_ON(i915_vma_misplaced(vma, size, alignment, flags));
> -		return 0;
> -	}
> -
> -	return __i915_vma_do_pin(vma, size, alignment, flags);
> -}
> +int __must_check
> +i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags);
>   
>   static inline int i915_vma_pin_count(const struct i915_vma *vma)
>   {
> @@ -366,17 +360,17 @@ static inline bool i915_vma_is_pinned(const struct i915_vma *vma)
>   static inline void __i915_vma_pin(struct i915_vma *vma)
>   {
>   	atomic_inc(&vma->flags);
> -	GEM_BUG_ON(atomic_read(&vma->flags) & I915_VMA_PIN_OVERFLOW);
> +	GEM_BUG_ON(!i915_vma_is_pinned(vma));
>   }
>   
>   static inline void __i915_vma_unpin(struct i915_vma *vma)
>   {
> +	GEM_BUG_ON(!i915_vma_is_pinned(vma));
>   	atomic_dec(&vma->flags);
>   }
>   
>   static inline void i915_vma_unpin(struct i915_vma *vma)
>   {
> -	GEM_BUG_ON(!i915_vma_is_pinned(vma));
>   	GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
>   	__i915_vma_unpin(vma);
>   }
> @@ -396,8 +390,6 @@ static inline bool i915_vma_is_bound(const struct i915_vma *vma,
>    * the caller must call i915_vma_unpin_iomap to relinquish the pinning
>    * after the iomapping is no longer required.
>    *
> - * Callers must hold the struct_mutex.
> - *
>    * Returns a valid iomapped pointer or ERR_PTR.
>    */
>   void __iomem *i915_vma_pin_iomap(struct i915_vma *vma);
> @@ -409,8 +401,8 @@ void __iomem *i915_vma_pin_iomap(struct i915_vma *vma);
>    *
>    * Unpins the previously iomapped VMA from i915_vma_pin_iomap().
>    *
> - * Callers must hold the struct_mutex. This function is only valid to be
> - * called on a VMA previously iomapped by the caller with i915_vma_pin_iomap().
> + * This function is only valid to be called on a VMA previously
> + * iomapped by the caller with i915_vma_pin_iomap().
>    */
>   void i915_vma_unpin_iomap(struct i915_vma *vma);
>   
> @@ -438,6 +430,8 @@ static inline struct page *i915_vma_first_page(struct i915_vma *vma)
>   int __must_check i915_vma_pin_fence(struct i915_vma *vma);
>   int __must_check i915_vma_revoke_fence(struct i915_vma *vma);
>   
> +int __i915_vma_pin_fence(struct i915_vma *vma);
> +
>   static inline void __i915_vma_unpin_fence(struct i915_vma *vma)
>   {
>   	GEM_BUG_ON(atomic_read(&vma->fence->pin_count) <= 0);
> @@ -455,7 +449,6 @@ static inline void __i915_vma_unpin_fence(struct i915_vma *vma)
>   static inline void
>   i915_vma_unpin_fence(struct i915_vma *vma)
>   {
> -	/* lockdep_assert_held(&vma->vm->i915->drm.struct_mutex); */
>   	if (vma->fence)
>   		__i915_vma_unpin_fence(vma);
>   }
> diff --git a/drivers/gpu/drm/i915/selftests/i915_gem_evict.c b/drivers/gpu/drm/i915/selftests/i915_gem_evict.c
> index cb30c669b1b7..ba6064147173 100644
> --- a/drivers/gpu/drm/i915/selftests/i915_gem_evict.c
> +++ b/drivers/gpu/drm/i915/selftests/i915_gem_evict.c
> @@ -106,14 +106,11 @@ static int populate_ggtt(struct drm_i915_private *i915,
>   
>   static void unpin_ggtt(struct drm_i915_private *i915)
>   {
> -	struct i915_ggtt *ggtt = &i915->ggtt;
>   	struct i915_vma *vma;
>   
> -	mutex_lock(&ggtt->vm.mutex);
>   	list_for_each_entry(vma, &i915->ggtt.vm.bound_list, vm_link)
>   		if (vma->obj->mm.quirked)
>   			i915_vma_unpin(vma);
> -	mutex_unlock(&ggtt->vm.mutex);
>   }
>   
>   static void cleanup_objects(struct drm_i915_private *i915,
> @@ -127,11 +124,7 @@ static void cleanup_objects(struct drm_i915_private *i915,
>   		i915_gem_object_put(obj);
>   	}
>   
> -	mutex_unlock(&i915->drm.struct_mutex);
> -
>   	i915_gem_drain_freed_objects(i915);
> -
> -	mutex_lock(&i915->drm.struct_mutex);
>   }
>   
>   static int igt_evict_something(void *arg)
> @@ -148,10 +141,12 @@ static int igt_evict_something(void *arg)
>   		goto cleanup;
>   
>   	/* Everything is pinned, nothing should happen */
> +	mutex_lock(&ggtt->vm.mutex);
>   	err = i915_gem_evict_something(&ggtt->vm,
>   				       I915_GTT_PAGE_SIZE, 0, 0,
>   				       0, U64_MAX,
>   				       0);
> +	mutex_unlock(&ggtt->vm.mutex);
>   	if (err != -ENOSPC) {
>   		pr_err("i915_gem_evict_something failed on a full GGTT with err=%d\n",
>   		       err);
> @@ -161,10 +156,12 @@ static int igt_evict_something(void *arg)
>   	unpin_ggtt(i915);
>   
>   	/* Everything is unpinned, we should be able to evict something */
> +	mutex_lock(&ggtt->vm.mutex);
>   	err = i915_gem_evict_something(&ggtt->vm,
>   				       I915_GTT_PAGE_SIZE, 0, 0,
>   				       0, U64_MAX,
>   				       0);
> +	mutex_unlock(&ggtt->vm.mutex);
>   	if (err) {
>   		pr_err("i915_gem_evict_something failed on a full GGTT with err=%d\n",
>   		       err);
> @@ -230,7 +227,9 @@ static int igt_evict_for_vma(void *arg)
>   		goto cleanup;
>   
>   	/* Everything is pinned, nothing should happen */
> +	mutex_lock(&ggtt->vm.mutex);
>   	err = i915_gem_evict_for_node(&ggtt->vm, &target, 0);
> +	mutex_unlock(&ggtt->vm.mutex);
>   	if (err != -ENOSPC) {
>   		pr_err("i915_gem_evict_for_node on a full GGTT returned err=%d\n",
>   		       err);
> @@ -240,7 +239,9 @@ static int igt_evict_for_vma(void *arg)
>   	unpin_ggtt(i915);
>   
>   	/* Everything is unpinned, we should be able to evict the node */
> +	mutex_lock(&ggtt->vm.mutex);
>   	err = i915_gem_evict_for_node(&ggtt->vm, &target, 0);
> +	mutex_unlock(&ggtt->vm.mutex);
>   	if (err) {
>   		pr_err("i915_gem_evict_for_node returned err=%d\n",
>   		       err);
> @@ -317,7 +318,9 @@ static int igt_evict_for_cache_color(void *arg)
>   	i915_vma_unpin(vma);
>   
>   	/* Remove just the second vma */
> +	mutex_lock(&ggtt->vm.mutex);
>   	err = i915_gem_evict_for_node(&ggtt->vm, &target, 0);
> +	mutex_unlock(&ggtt->vm.mutex);
>   	if (err) {
>   		pr_err("[0]i915_gem_evict_for_node returned err=%d\n", err);
>   		goto cleanup;
> @@ -328,7 +331,9 @@ static int igt_evict_for_cache_color(void *arg)
>   	 */
>   	target.color = I915_CACHE_L3_LLC;
>   
> +	mutex_lock(&ggtt->vm.mutex);
>   	err = i915_gem_evict_for_node(&ggtt->vm, &target, 0);
> +	mutex_unlock(&ggtt->vm.mutex);
>   	if (!err) {
>   		pr_err("[1]i915_gem_evict_for_node returned err=%d\n", err);
>   		err = -EINVAL;
> @@ -358,7 +363,9 @@ static int igt_evict_vm(void *arg)
>   		goto cleanup;
>   
>   	/* Everything is pinned, nothing should happen */
> +	mutex_lock(&ggtt->vm.mutex);
>   	err = i915_gem_evict_vm(&ggtt->vm);
> +	mutex_unlock(&ggtt->vm.mutex);
>   	if (err) {
>   		pr_err("i915_gem_evict_vm on a full GGTT returned err=%d]\n",
>   		       err);
> @@ -367,7 +374,9 @@ static int igt_evict_vm(void *arg)
>   
>   	unpin_ggtt(i915);
>   
> +	mutex_lock(&ggtt->vm.mutex);
>   	err = i915_gem_evict_vm(&ggtt->vm);
> +	mutex_unlock(&ggtt->vm.mutex);
>   	if (err) {
>   		pr_err("i915_gem_evict_vm on a full GGTT returned err=%d]\n",
>   		       err);
> @@ -408,11 +417,11 @@ static int igt_evict_contexts(void *arg)
>   	if (!HAS_FULL_PPGTT(i915))
>   		return 0;
>   
> -	mutex_lock(&i915->drm.struct_mutex);
>   	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
>   
>   	/* Reserve a block so that we know we have enough to fit a few rq */
>   	memset(&hole, 0, sizeof(hole));
> +	mutex_lock(&i915->ggtt.vm.mutex);
>   	err = i915_gem_gtt_insert(&i915->ggtt.vm, &hole,
>   				  PRETEND_GGTT_SIZE, 0, I915_COLOR_UNEVICTABLE,
>   				  0, i915->ggtt.vm.total,
> @@ -425,7 +434,9 @@ static int igt_evict_contexts(void *arg)
>   	do {
>   		struct reserved *r;
>   
> +		mutex_unlock(&i915->ggtt.vm.mutex);
>   		r = kcalloc(1, sizeof(*r), GFP_KERNEL);
> +		mutex_lock(&i915->ggtt.vm.mutex);
>   		if (!r) {
>   			err = -ENOMEM;
>   			goto out_locked;
> @@ -445,7 +456,7 @@ static int igt_evict_contexts(void *arg)
>   		count++;
>   	} while (1);
>   	drm_mm_remove_node(&hole);
> -	mutex_unlock(&i915->drm.struct_mutex);
> +	mutex_unlock(&i915->ggtt.vm.mutex);
>   	pr_info("Filled GGTT with %lu 1MiB nodes\n", count);
>   
>   	/* Overfill the GGTT with context objects and so try to evict one. */
> @@ -508,7 +519,7 @@ static int igt_evict_contexts(void *arg)
>   			break;
>   	}
>   
> -	mutex_lock(&i915->drm.struct_mutex);
> +	mutex_lock(&i915->ggtt.vm.mutex);
>   out_locked:
>   	if (igt_flush_test(i915, I915_WAIT_LOCKED))
>   		err = -EIO;
> @@ -522,8 +533,8 @@ static int igt_evict_contexts(void *arg)
>   	}
>   	if (drm_mm_node_allocated(&hole))
>   		drm_mm_remove_node(&hole);
> +	mutex_unlock(&i915->ggtt.vm.mutex);
>   	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
> -	mutex_unlock(&i915->drm.struct_mutex);
>   
>   	return err;
>   }
> @@ -545,12 +556,9 @@ int i915_gem_evict_mock_selftests(void)
>   	if (!i915)
>   		return -ENOMEM;
>   
> -	mutex_lock(&i915->drm.struct_mutex);
>   	with_intel_runtime_pm(&i915->runtime_pm, wakeref)
>   		err = i915_subtests(tests, i915);
>   
> -	mutex_unlock(&i915->drm.struct_mutex);
> -
>   	drm_dev_put(&i915->drm);
>   	return err;
>   }
> diff --git a/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c b/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
> index a90c9be95f8c..4235fa401956 100644
> --- a/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
> +++ b/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
> @@ -35,16 +35,7 @@
>   
>   static void cleanup_freed_objects(struct drm_i915_private *i915)
>   {
> -	/*
> -	 * As we may hold onto the struct_mutex for inordinate lengths of
> -	 * time, the NMI khungtaskd detector may fire for the free objects
> -	 * worker.
> -	 */
> -	mutex_unlock(&i915->drm.struct_mutex);
> -
>   	i915_gem_drain_freed_objects(i915);
> -
> -	mutex_lock(&i915->drm.struct_mutex);
>   }
>   
>   static void fake_free_pages(struct drm_i915_gem_object *obj,
> @@ -318,6 +309,17 @@ static int lowlevel_hole(struct drm_i915_private *i915,
>   	return 0;
>   }
>   
> +static int unlocked_vma_unbind(struct i915_vma *vma)
> +{
> +	int ret;
> +
> +	mutex_lock(&vma->vm->mutex);
> +	ret = i915_vma_unbind(vma);
> +	mutex_unlock(&vma->vm->mutex);
> +
> +	return ret;
> +}
> +
>   static void close_object_list(struct list_head *objects,
>   			      struct i915_address_space *vm)
>   {
> @@ -329,7 +331,7 @@ static void close_object_list(struct list_head *objects,
>   
>   		vma = i915_vma_instance(obj, vm, NULL);
>   		if (!IS_ERR(vma))
> -			ignored = i915_vma_unbind(vma);
> +			ignored = unlocked_vma_unbind(vma);
>   		/* Only ppgtt vma may be closed before the object is freed */
>   		if (!IS_ERR(vma) && !i915_vma_is_ggtt(vma))
>   			i915_vma_close(vma);
> @@ -444,7 +446,7 @@ static int fill_hole(struct drm_i915_private *i915,
>   						goto err;
>   					}
>   
> -					err = i915_vma_unbind(vma);
> +					err = unlocked_vma_unbind(vma);
>   					if (err) {
>   						pr_err("%s(%s) (forward) unbind of vma.node=%llx + %llx failed with err=%d\n",
>   						       __func__, p->name, vma->node.start, vma->node.size,
> @@ -517,7 +519,7 @@ static int fill_hole(struct drm_i915_private *i915,
>   						goto err;
>   					}
>   
> -					err = i915_vma_unbind(vma);
> +					err = unlocked_vma_unbind(vma);
>   					if (err) {
>   						pr_err("%s(%s) (backward) unbind of vma.node=%llx + %llx failed with err=%d\n",
>   						       __func__, p->name, vma->node.start, vma->node.size,
> @@ -604,7 +606,7 @@ static int walk_hole(struct drm_i915_private *i915,
>   				goto err_close;
>   			}
>   
> -			err = i915_vma_unbind(vma);
> +			err = unlocked_vma_unbind(vma);
>   			if (err) {
>   				pr_err("%s unbind failed at %llx + %llx  with err=%d\n",
>   				       __func__, addr, vma->size, err);
> @@ -685,13 +687,13 @@ static int pot_hole(struct drm_i915_private *i915,
>   				pr_err("%s incorrect at %llx + %llx\n",
>   				       __func__, addr, vma->size);
>   				i915_vma_unpin(vma);
> -				err = i915_vma_unbind(vma);
> +				err = unlocked_vma_unbind(vma);
>   				err = -EINVAL;
>   				goto err;
>   			}
>   
>   			i915_vma_unpin(vma);
> -			err = i915_vma_unbind(vma);
> +			err = unlocked_vma_unbind(vma);
>   			GEM_BUG_ON(err);
>   		}
>   
> @@ -789,13 +791,13 @@ static int drunk_hole(struct drm_i915_private *i915,
>   				pr_err("%s incorrect at %llx + %llx\n",
>   				       __func__, addr, BIT_ULL(size));
>   				i915_vma_unpin(vma);
> -				err = i915_vma_unbind(vma);
> +				err = unlocked_vma_unbind(vma);
>   				err = -EINVAL;
>   				goto err;
>   			}
>   
>   			i915_vma_unpin(vma);
> -			err = i915_vma_unbind(vma);
> +			err = unlocked_vma_unbind(vma);
>   			GEM_BUG_ON(err);
>   
>   			if (igt_timeout(end_time,
> @@ -867,7 +869,7 @@ static int __shrink_hole(struct drm_i915_private *i915,
>   			pr_err("%s incorrect at %llx + %llx\n",
>   			       __func__, addr, size);
>   			i915_vma_unpin(vma);
> -			err = i915_vma_unbind(vma);
> +			err = unlocked_vma_unbind(vma);
>   			err = -EINVAL;
>   			break;
>   		}
> @@ -875,6 +877,15 @@ static int __shrink_hole(struct drm_i915_private *i915,
>   		i915_vma_unpin(vma);
>   		addr += size;
>   
> +		/*
> +		 * Since we are injecting allocation faults at random intervals,
> +		 * wait for this allocation to complete before we change the
> +		 * faultinjection.
> +		 */
> +		err = i915_active_wait(&vma->active);
> +		if (err)
> +			break;
> +
>   		if (igt_timeout(end_time,
>   				"%s timed out at ofset %llx [%llx - %llx]\n",
>   				__func__, addr, hole_start, hole_end)) {
> @@ -1008,21 +1019,19 @@ static int exercise_ppgtt(struct drm_i915_private *dev_priv,
>   	if (IS_ERR(file))
>   		return PTR_ERR(file);
>   
> -	mutex_lock(&dev_priv->drm.struct_mutex);
>   	ppgtt = i915_ppgtt_create(dev_priv);
>   	if (IS_ERR(ppgtt)) {
>   		err = PTR_ERR(ppgtt);
> -		goto out_unlock;
> +		goto out_free;
>   	}
>   	GEM_BUG_ON(offset_in_page(ppgtt->vm.total));
> -	GEM_BUG_ON(ppgtt->vm.closed);
> +	GEM_BUG_ON(!atomic_read(&ppgtt->vm.open));
>   
>   	err = func(dev_priv, &ppgtt->vm, 0, ppgtt->vm.total, end_time);
>   
>   	i915_vm_put(&ppgtt->vm);
> -out_unlock:
> -	mutex_unlock(&dev_priv->drm.struct_mutex);
>   
> +out_free:
>   	mock_file_free(dev_priv, file);
>   	return err;
>   }
> @@ -1085,7 +1094,6 @@ static int exercise_ggtt(struct drm_i915_private *i915,
>   	IGT_TIMEOUT(end_time);
>   	int err = 0;
>   
> -	mutex_lock(&i915->drm.struct_mutex);
>   restart:
>   	list_sort(NULL, &ggtt->vm.mm.hole_stack, sort_holes);
>   	drm_mm_for_each_hole(node, &ggtt->vm.mm, hole_start, hole_end) {
> @@ -1106,7 +1114,6 @@ static int exercise_ggtt(struct drm_i915_private *i915,
>   		last = hole_end;
>   		goto restart;
>   	}
> -	mutex_unlock(&i915->drm.struct_mutex);
>   
>   	return err;
>   }
> @@ -1148,13 +1155,9 @@ static int igt_ggtt_page(void *arg)
>   	unsigned int *order, n;
>   	int err;
>   
> -	mutex_lock(&i915->drm.struct_mutex);
> -
>   	obj = i915_gem_object_create_internal(i915, PAGE_SIZE);
> -	if (IS_ERR(obj)) {
> -		err = PTR_ERR(obj);
> -		goto out_unlock;
> -	}
> +	if (IS_ERR(obj))
> +		return PTR_ERR(obj);
>   
>   	err = i915_gem_object_pin_pages(obj);
>   	if (err)
> @@ -1222,8 +1225,6 @@ static int igt_ggtt_page(void *arg)
>   	i915_gem_object_unpin_pages(obj);
>   out_free:
>   	i915_gem_object_put(obj);
> -out_unlock:
> -	mutex_unlock(&i915->drm.struct_mutex);
>   	return err;
>   }
>   
> @@ -1234,6 +1235,9 @@ static void track_vma_bind(struct i915_vma *vma)
>   	atomic_inc(&obj->bind_count); /* track for eviction later */
>   	__i915_gem_object_pin_pages(obj);
>   
> +	GEM_BUG_ON(vma->pages);
> +	atomic_set(&vma->pages_count, I915_VMA_PAGES_ACTIVE);
> +	__i915_gem_object_pin_pages(obj);
>   	vma->pages = obj->mm.pages;
>   
>   	mutex_lock(&vma->vm->mutex);
> @@ -1330,11 +1334,13 @@ static int igt_gtt_reserve(void *arg)
>   			goto out;
>   		}
>   
> +		mutex_lock(&ggtt->vm.mutex);
>   		err = i915_gem_gtt_reserve(&ggtt->vm, &vma->node,
>   					   obj->base.size,
>   					   total,
>   					   obj->cache_level,
>   					   0);
> +		mutex_unlock(&ggtt->vm.mutex);
>   		if (err) {
>   			pr_err("i915_gem_gtt_reserve (pass 1) failed at %llu/%llu with err=%d\n",
>   			       total, ggtt->vm.total, err);
> @@ -1380,11 +1386,13 @@ static int igt_gtt_reserve(void *arg)
>   			goto out;
>   		}
>   
> +		mutex_lock(&ggtt->vm.mutex);
>   		err = i915_gem_gtt_reserve(&ggtt->vm, &vma->node,
>   					   obj->base.size,
>   					   total,
>   					   obj->cache_level,
>   					   0);
> +		mutex_unlock(&ggtt->vm.mutex);
>   		if (err) {
>   			pr_err("i915_gem_gtt_reserve (pass 2) failed at %llu/%llu with err=%d\n",
>   			       total, ggtt->vm.total, err);
> @@ -1414,7 +1422,7 @@ static int igt_gtt_reserve(void *arg)
>   			goto out;
>   		}
>   
> -		err = i915_vma_unbind(vma);
> +		err = unlocked_vma_unbind(vma);
>   		if (err) {
>   			pr_err("i915_vma_unbind failed with err=%d!\n", err);
>   			goto out;
> @@ -1424,11 +1432,13 @@ static int igt_gtt_reserve(void *arg)
>   				       2*I915_GTT_PAGE_SIZE,
>   				       I915_GTT_MIN_ALIGNMENT);
>   
> +		mutex_lock(&ggtt->vm.mutex);
>   		err = i915_gem_gtt_reserve(&ggtt->vm, &vma->node,
>   					   obj->base.size,
>   					   offset,
>   					   obj->cache_level,
>   					   0);
> +		mutex_unlock(&ggtt->vm.mutex);
>   		if (err) {
>   			pr_err("i915_gem_gtt_reserve (pass 3) failed at %llu/%llu with err=%d\n",
>   			       total, ggtt->vm.total, err);
> @@ -1497,11 +1507,13 @@ static int igt_gtt_insert(void *arg)
>   
>   	/* Check a couple of obviously invalid requests */
>   	for (ii = invalid_insert; ii->size; ii++) {
> +		mutex_lock(&ggtt->vm.mutex);
>   		err = i915_gem_gtt_insert(&ggtt->vm, &tmp,
>   					  ii->size, ii->alignment,
>   					  I915_COLOR_UNEVICTABLE,
>   					  ii->start, ii->end,
>   					  0);
> +		mutex_unlock(&ggtt->vm.mutex);
>   		if (err != -ENOSPC) {
>   			pr_err("Invalid i915_gem_gtt_insert(.size=%llx, .alignment=%llx, .start=%llx, .end=%llx) succeeded (err=%d)\n",
>   			       ii->size, ii->alignment, ii->start, ii->end,
> @@ -1537,10 +1549,12 @@ static int igt_gtt_insert(void *arg)
>   			goto out;
>   		}
>   
> +		mutex_lock(&ggtt->vm.mutex);
>   		err = i915_gem_gtt_insert(&ggtt->vm, &vma->node,
>   					  obj->base.size, 0, obj->cache_level,
>   					  0, ggtt->vm.total,
>   					  0);
> +		mutex_unlock(&ggtt->vm.mutex);
>   		if (err == -ENOSPC) {
>   			/* maxed out the GGTT space */
>   			i915_gem_object_put(obj);
> @@ -1589,16 +1603,18 @@ static int igt_gtt_insert(void *arg)
>   		GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
>   		offset = vma->node.start;
>   
> -		err = i915_vma_unbind(vma);
> +		err = unlocked_vma_unbind(vma);
>   		if (err) {
>   			pr_err("i915_vma_unbind failed with err=%d!\n", err);
>   			goto out;
>   		}
>   
> +		mutex_lock(&ggtt->vm.mutex);
>   		err = i915_gem_gtt_insert(&ggtt->vm, &vma->node,
>   					  obj->base.size, 0, obj->cache_level,
>   					  0, ggtt->vm.total,
>   					  0);
> +		mutex_unlock(&ggtt->vm.mutex);
>   		if (err) {
>   			pr_err("i915_gem_gtt_insert (pass 2) failed at %llu/%llu with err=%d\n",
>   			       total, ggtt->vm.total, err);
> @@ -1642,10 +1658,12 @@ static int igt_gtt_insert(void *arg)
>   			goto out;
>   		}
>   
> +		mutex_lock(&ggtt->vm.mutex);
>   		err = i915_gem_gtt_insert(&ggtt->vm, &vma->node,
>   					  obj->base.size, 0, obj->cache_level,
>   					  0, ggtt->vm.total,
>   					  0);
> +		mutex_unlock(&ggtt->vm.mutex);
>   		if (err) {
>   			pr_err("i915_gem_gtt_insert (pass 3) failed at %llu/%llu with err=%d\n",
>   			       total, ggtt->vm.total, err);
> @@ -1689,8 +1707,9 @@ int i915_gem_gtt_mock_selftests(void)
>   	}
>   	mock_init_ggtt(i915, ggtt);
>   
> -	mutex_lock(&i915->drm.struct_mutex);
>   	err = i915_subtests(tests, ggtt);
> +
> +	mutex_lock(&i915->drm.struct_mutex);
>   	mock_device_flush(i915);
>   	mutex_unlock(&i915->drm.struct_mutex);
>   
> diff --git a/drivers/gpu/drm/i915/selftests/i915_vma.c b/drivers/gpu/drm/i915/selftests/i915_vma.c
> index 97752deecccb..e0ca12c17a7f 100644
> --- a/drivers/gpu/drm/i915/selftests/i915_vma.c
> +++ b/drivers/gpu/drm/i915/selftests/i915_vma.c
> @@ -337,7 +337,9 @@ static int igt_vma_pin1(void *arg)
>   
>   		if (!err) {
>   			i915_vma_unpin(vma);
> +			mutex_lock(&ggtt->vm.mutex);
>   			err = i915_vma_unbind(vma);
> +			mutex_unlock(&ggtt->vm.mutex);
>   			if (err) {
>   				pr_err("Failed to unbind single page from GGTT, err=%d\n", err);
>   				goto out;
> @@ -831,8 +833,9 @@ int i915_vma_mock_selftests(void)
>   	}
>   	mock_init_ggtt(i915, ggtt);
>   
> -	mutex_lock(&i915->drm.struct_mutex);
>   	err = i915_subtests(tests, ggtt);
> +
> +	mutex_lock(&i915->drm.struct_mutex);
>   	mock_device_flush(i915);
>   	mutex_unlock(&i915->drm.struct_mutex);
>   
> @@ -879,8 +882,6 @@ static int igt_vma_remapped_gtt(void *arg)
>   	if (IS_ERR(obj))
>   		return PTR_ERR(obj);
>   
> -	mutex_lock(&i915->drm.struct_mutex);
> -
>   	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
>   
>   	for (t = types; *t; t++) {
> @@ -976,7 +977,6 @@ static int igt_vma_remapped_gtt(void *arg)
>   
>   out:
>   	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
> -	mutex_unlock(&i915->drm.struct_mutex);
>   	i915_gem_object_put(obj);
>   
>   	return err;
> 
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* Re: [PATCH 13/21] drm/i915: Pull i915_vma_pin under the vm->mutex
  2019-09-16 10:13   ` Tvrtko Ursulin
@ 2019-09-16 11:10     ` Chris Wilson
  0 siblings, 0 replies; 53+ messages in thread
From: Chris Wilson @ 2019-09-16 11:10 UTC (permalink / raw)
  To: Tvrtko Ursulin, intel-gfx

Quoting Tvrtko Ursulin (2019-09-16 11:13:23)
> 
> On 02/09/2019 05:02, Chris Wilson wrote:
> > Replace the struct_mutex requirement for pinning the i915_vma with the
> > local vm->mutex instead. Note that the vm->mutex is tainted by the
> > shrinker (we require unbinding from inside fs-reclaim) and so we cannot
> > allocate while holding that mutex. Instead we have to preallocate
> > workers to do allocate and apply the PTE updates after we have we
> > reserved their slot in the drm_mm (using fences to order the PTE writes
> > with the GPU work and with later unbind).
> 
> Can you put some paragraphs into the commit describing the 
> infrastructure changes? Like changes to active tracker at least.

You mean the addition of the preallocated node?

> Then commentary on effects on shrinker, fences, object management, 
> execbuf, basically all major parts of the code which have now been 
> fundamentally improved or changed. It would help with review. It's a 
> chunky patch/change and I think it needs some theory of operation text.

Off the top of my held, they are all just lock switches. The funky stuff
lies around avoiding introducing a kref for i915_vma and that requires a
whole bunch of trylocks to avoid double-frees. (I did plan on having a
kref here, but that leads to rewriting object page tracking which is in
its own quagmire.)

> > 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 f99920652751..9e72b42a86f5 100644
> > --- a/drivers/gpu/drm/i915/gem/i915_gem_client_blt.c
> > +++ b/drivers/gpu/drm/i915/gem/i915_gem_client_blt.c
> > @@ -152,6 +152,17 @@ static void clear_pages_dma_fence_cb(struct dma_fence *fence,
> >       irq_work_queue(&w->irq_work);
> >   }
> >   
> > +static int move_to_active(struct i915_vma *vma, struct i915_request *rq)
> > +{
> > +     int err;
> > +
> > +     err = i915_request_await_active(rq, &vma->active);
> > +     if (err)
> > +             return err;
> > +
> > +     return i915_active_ref(&vma->active, rq->timeline, rq);
> > +}
> > +
> >   static void clear_pages_worker(struct work_struct *work)
> >   {
> >       struct clear_pages_work *w = container_of(work, typeof(*w), work);
> > @@ -211,7 +222,7 @@ static void clear_pages_worker(struct work_struct *work)
> >        * keep track of the GPU activity within this vma/request, and
> >        * propagate the signal from the request to w->dma.
> >        */
> > -     err = i915_active_ref(&vma->active, rq->timeline, rq);
> > +     err = move_to_active(vma, rq);
> 
> What is happening here? It wasn't sufficiently ordered before or 
> something changes with this patch?

We are introducing an independent ordering constraints. The clear page
worker is itself interwined with the obj->resv but one part of the
operation must be serialised with the asynchronous binding, and we don't
want that async binding to affect the object globally. So the clear-page
workers opencodes the awaits from i915_vma_move_to_active, but does not
want to partake in the fence export (as the operations is already in the
obj->resv).

> > diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context.c b/drivers/gpu/drm/i915/gem/i915_gem_context.c
> > index f1c0e5d958f3..653f7275306a 100644
> > --- a/drivers/gpu/drm/i915/gem/i915_gem_context.c
> > +++ b/drivers/gpu/drm/i915/gem/i915_gem_context.c
> > @@ -313,8 +313,6 @@ static void i915_gem_context_free(struct i915_gem_context *ctx)
> >       GEM_BUG_ON(!i915_gem_context_is_closed(ctx));
> >   
> >       release_hw_id(ctx);
> > -     if (ctx->vm)
> > -             i915_vm_put(ctx->vm);
> 
> Contexts no longer hold a reference to vm? Only vmas?

Dropped earlier on close rather than final put...

> > @@ -379,9 +377,13 @@ void i915_gem_context_release(struct kref *ref)
> >   
> >   static void context_close(struct i915_gem_context *ctx)
> >   {
> > +     i915_gem_context_set_closed(ctx);
> > +
> > +     if (ctx->vm)
> > +             i915_vm_close(ctx->vm);
> 
> But now closed context mean closed vm, but what about shared vms? Is 
> open/close_vm now counted?

Yes. vm->open_count. This is all to avoid double-free of the vma from
object-close and vm-close. But closing earlier tidies up the vm release
and restores some earlier behaviour to avoid unbinding from a dead vm.

> > -                     ret = i915_vma_bind(vma, cache_level, PIN_UPDATE);
> > +                     /* Wait for an earlier async bind */
> > +                     ret = i915_active_wait(&vma->active);
> > +                     if (ret)
> > +                             return ret;
> > +
> > +                     ret = i915_vma_bind(vma, cache_level, PIN_UPDATE, NULL);
> 
> Waiting should not be implied in the bind? I am assuming at least there 
> will be many callers to bind and like this all of them will have to know 
> to do i915_active_wait first?

Not really, imo, i915_vma_bind() is a special case as it is the
low-level operation. And we only need the sync here as we plan to
rewrite those ongoing operations; in the other (non-UPDATE) caller, we
are adding a separate binding rather than overwriting existing.

> > @@ -483,6 +487,7 @@ static void i915_gem_object_bump_inactive_ggtt(struct drm_i915_gem_object *obj)
> >               if (!drm_mm_node_allocated(&vma->node))
> >                       continue;
> >   
> > +             GEM_BUG_ON(vma->vm != &i915->ggtt.vm);
> >               list_move_tail(&vma->vm_link, &vma->vm->bound_list);
> >       }
> >       mutex_unlock(&i915->ggtt.vm.mutex);
> > diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> > index c049199a1df5..c3dd8ce7e2b7 100644
> > --- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> > +++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> > @@ -550,8 +550,11 @@ eb_add_vma(struct i915_execbuffer *eb,
> >               eb_unreserve_vma(vma, vma->exec_flags);
> >   
> >               list_add_tail(&vma->exec_link, &eb->unbound);
> > -             if (drm_mm_node_allocated(&vma->node))
> > +             if (drm_mm_node_allocated(&vma->node)) {
> > +                     mutex_lock(&vma->vm->mutex);
> >                       err = i915_vma_unbind(vma);
> > +                     mutex_unlock(&vma->vm->mutex);
> 
> Should there be __i915_vma_unbind which asserts the lock and 
> i915_vma_unbind which takes it?

Could do, it seems to be an even split. I had a goal that
i915_vma_unbind() should evaporate; it should become a pipelined
operation for the most part, just the shrinker being always an exception
as our interface there is external.

> > +void i915_active_set_exclusive(struct i915_active *ref, struct dma_fence *f)
> > +{
> > +     GEM_BUG_ON(i915_active_is_idle(ref));
> > +
> > +     dma_fence_get(f);
> > +
> > +     rcu_read_lock();
> > +     if (rcu_access_pointer(ref->excl)) {
> > +             struct dma_fence *old;
> > +
> > +             old = dma_fence_get_rcu_safe(&ref->excl);
> > +             if (old) {
> > +                     if (dma_fence_remove_callback(old, &ref->excl_cb))
> > +                             atomic_dec(&ref->count);
> > +                     dma_fence_put(old);
> > +             }
> > +     }
> > +     rcu_read_unlock();
> > +
> > +     atomic_inc(&ref->count);
> > +     rcu_assign_pointer(ref->excl, f);
> > +
> > +     if (dma_fence_add_callback(f, &ref->excl_cb, excl_cb)) {
> > +             RCU_INIT_POINTER(ref->excl, NULL);
> > +             atomic_dec(&ref->count);
> > +             dma_fence_put(f);
> 
> Is this some silent failure path?

No, it's just a weird indication by dma-fence that the fence was already
signaled.

> >   int i915_request_await_active(struct i915_request *rq, struct i915_active *ref)
> >   {
> > -     struct active_node *it, *n;
> > -     int err;
> > -
> > -     if (RB_EMPTY_ROOT(&ref->tree))
> > -             return 0;
> > +     int err = 0;
> >   
> > -     /* await allocates and so we need to avoid hitting the shrinker */
> > -     err = i915_active_acquire(ref);
> > -     if (err)
> > -             return err;
> > +     if (rcu_access_pointer(ref->excl)) {
> > +             struct dma_fence *fence;
> >   
> > -     mutex_lock(&ref->mutex);
> > -     rbtree_postorder_for_each_entry_safe(it, n, &ref->tree, node) {
> > -             err = i915_request_await_active_request(rq, &it->base);
> > -             if (err)
> > -                     break;
> > +             rcu_read_lock();
> > +             fence = dma_fence_get_rcu_safe(&ref->excl);
> > +             rcu_read_unlock();
> > +             if (fence) {
> > +                     err = i915_request_await_dma_fence(rq, fence);
> > +                     dma_fence_put(fence);
> > +             }
> >       }
> > -     mutex_unlock(&ref->mutex);
> >   
> > -     i915_active_release(ref);
> > +     /* In the future we may choose to await on all fences */
> > +
> 
> This is completely changed to just wait on the "exclusive" fence? What 
> about the tree of nodes? It's still in the data structure and managed 
> elsewhere. Not needed any more until "future" from the comment?

It was all dead code. The use case I have for it atm is purely about
managing that exclusive timeline.
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* Re: [PATCH 13/21] drm/i915: Pull i915_vma_pin under the vm->mutex
  2019-09-02  4:02 ` [PATCH 13/21] drm/i915: Pull i915_vma_pin under the vm->mutex Chris Wilson
  2019-09-16 10:13   ` Tvrtko Ursulin
@ 2019-09-17 12:37   ` Tvrtko Ursulin
  2019-09-17 18:56     ` Chris Wilson
  1 sibling, 1 reply; 53+ messages in thread
From: Tvrtko Ursulin @ 2019-09-17 12:37 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx



On 02/09/2019 05:02, Chris Wilson wrote:
> Replace the struct_mutex requirement for pinning the i915_vma with the
> local vm->mutex instead. Note that the vm->mutex is tainted by the
> shrinker (we require unbinding from inside fs-reclaim) and so we cannot
> allocate while holding that mutex. Instead we have to preallocate
> workers to do allocate and apply the PTE updates after we have we
> reserved their slot in the drm_mm (using fences to order the PTE writes
> with the GPU work and with later unbind).
> 
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> ---
>   drivers/gpu/drm/i915/display/intel_display.c  |  29 +-
>   drivers/gpu/drm/i915/display/intel_fbdev.c    |   8 +-
>   drivers/gpu/drm/i915/display/intel_overlay.c  |  11 +-
>   .../gpu/drm/i915/gem/i915_gem_client_blt.c    |  13 +-
>   drivers/gpu/drm/i915/gem/i915_gem_context.c   |  20 +-
>   drivers/gpu/drm/i915/gem/i915_gem_domain.c    |  19 +-
>   .../gpu/drm/i915/gem/i915_gem_execbuffer.c    |  48 +-
>   drivers/gpu/drm/i915/gem/i915_gem_mman.c      |  24 +-
>   drivers/gpu/drm/i915/gem/i915_gem_object.c    |  33 +-
>   drivers/gpu/drm/i915/gem/i915_gem_object.h    |   5 +
>   drivers/gpu/drm/i915/gem/i915_gem_shrinker.c  |  71 +--
>   drivers/gpu/drm/i915/gem/i915_gem_stolen.c    |   8 +-
>   drivers/gpu/drm/i915/gem/i915_gem_tiling.c    |  15 +-
>   drivers/gpu/drm/i915/gem/i915_gem_userptr.c   |  27 +-
>   .../gpu/drm/i915/gem/selftests/huge_pages.c   |  23 +-
>   .../drm/i915/gem/selftests/i915_gem_context.c |  12 +-
>   .../drm/i915/gem/selftests/i915_gem_mman.c    |   2 -
>   .../drm/i915/gem/selftests/igt_gem_utils.c    |   7 +-
>   drivers/gpu/drm/i915/gt/intel_gt.c            |   5 +-
>   drivers/gpu/drm/i915/gt/intel_ringbuffer.c    |   4 +-
>   drivers/gpu/drm/i915/gt/selftest_hangcheck.c  |  19 +-
>   drivers/gpu/drm/i915/gvt/aperture_gm.c        |  12 +-
>   drivers/gpu/drm/i915/i915_active.c            |  94 +++-
>   drivers/gpu/drm/i915/i915_active.h            |   7 +
>   drivers/gpu/drm/i915/i915_active_types.h      |   5 +
>   drivers/gpu/drm/i915/i915_gem.c               |  94 ++--
>   drivers/gpu/drm/i915/i915_gem_evict.c         |  20 +-
>   drivers/gpu/drm/i915/i915_gem_fence_reg.c     |   5 +-
>   drivers/gpu/drm/i915/i915_gem_gtt.c           | 104 ++--
>   drivers/gpu/drm/i915/i915_gem_gtt.h           |  38 +-
>   drivers/gpu/drm/i915/i915_perf.c              |  32 +-
>   drivers/gpu/drm/i915/i915_vma.c               | 476 ++++++++++++------
>   drivers/gpu/drm/i915/i915_vma.h               |  75 ++-
>   .../gpu/drm/i915/selftests/i915_gem_evict.c   |  36 +-
>   drivers/gpu/drm/i915/selftests/i915_gem_gtt.c |  91 ++--
>   drivers/gpu/drm/i915/selftests/i915_vma.c     |   8 +-
>   36 files changed, 818 insertions(+), 682 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c
> index 5e3b22e3f61d..81c0d7a35c4c 100644
> --- a/drivers/gpu/drm/i915/display/intel_display.c
> +++ b/drivers/gpu/drm/i915/display/intel_display.c
> @@ -2079,7 +2079,6 @@ intel_pin_and_fence_fb_obj(struct drm_framebuffer *fb,
>   	unsigned int pinctl;
>   	u32 alignment;
>   
> -	WARN_ON(!mutex_is_locked(&dev->struct_mutex));
>   	if (WARN_ON(!i915_gem_object_is_framebuffer(obj)))
>   		return ERR_PTR(-EINVAL);
>   
> @@ -2163,8 +2162,6 @@ intel_pin_and_fence_fb_obj(struct drm_framebuffer *fb,
>   
>   void intel_unpin_fb_vma(struct i915_vma *vma, unsigned long flags)
>   {
> -	lockdep_assert_held(&vma->vm->i915->drm.struct_mutex);
> -
>   	i915_gem_object_lock(vma->obj);
>   	if (flags & PLANE_HAS_FENCE)
>   		i915_vma_unpin_fence(vma);
> @@ -3065,12 +3062,10 @@ intel_alloc_initial_plane_obj(struct intel_crtc *crtc,
>   		return false;
>   	}
>   
> -	mutex_lock(&dev->struct_mutex);
>   	obj = i915_gem_object_create_stolen_for_preallocated(dev_priv,
>   							     base_aligned,
>   							     base_aligned,
>   							     size_aligned);
> -	mutex_unlock(&dev->struct_mutex);
>   	if (!obj)
>   		return false;
>   
> @@ -3232,13 +3227,11 @@ intel_find_initial_plane_obj(struct intel_crtc *intel_crtc,
>   	intel_state->color_plane[0].stride =
>   		intel_fb_pitch(fb, 0, intel_state->base.rotation);
>   
> -	mutex_lock(&dev->struct_mutex);
>   	intel_state->vma =
>   		intel_pin_and_fence_fb_obj(fb,
>   					   &intel_state->view,
>   					   intel_plane_uses_fence(intel_state),
>   					   &intel_state->flags);
> -	mutex_unlock(&dev->struct_mutex);
>   	if (IS_ERR(intel_state->vma)) {
>   		DRM_ERROR("failed to pin boot fb on pipe %d: %li\n",
>   			  intel_crtc->pipe, PTR_ERR(intel_state->vma));
> @@ -14364,8 +14357,6 @@ static void fb_obj_bump_render_priority(struct drm_i915_gem_object *obj)
>    * bits.  Some older platforms need special physical address handling for
>    * cursor planes.
>    *
> - * Must be called with struct_mutex held.
> - *
>    * Returns 0 on success, negative error code on failure.
>    */
>   int
> @@ -14422,15 +14413,8 @@ intel_prepare_plane_fb(struct drm_plane *plane,
>   	if (ret)
>   		return ret;
>   
> -	ret = mutex_lock_interruptible(&dev_priv->drm.struct_mutex);
> -	if (ret) {
> -		i915_gem_object_unpin_pages(obj);
> -		return ret;
> -	}
> -
>   	ret = intel_plane_pin_fb(to_intel_plane_state(new_state));
>   
> -	mutex_unlock(&dev_priv->drm.struct_mutex);
>   	i915_gem_object_unpin_pages(obj);
>   	if (ret)
>   		return ret;
> @@ -14479,8 +14463,6 @@ intel_prepare_plane_fb(struct drm_plane *plane,
>    * @old_state: the state from the previous modeset
>    *
>    * Cleans up a framebuffer that has just been removed from a plane.
> - *
> - * Must be called with struct_mutex held.
>    */
>   void
>   intel_cleanup_plane_fb(struct drm_plane *plane,
> @@ -14496,9 +14478,7 @@ intel_cleanup_plane_fb(struct drm_plane *plane,
>   	}
>   
>   	/* Should only be called after a successful intel_prepare_plane_fb()! */
> -	mutex_lock(&dev_priv->drm.struct_mutex);
>   	intel_plane_unpin_fb(to_intel_plane_state(old_state));
> -	mutex_unlock(&dev_priv->drm.struct_mutex);
>   }
>   
>   int
> @@ -14698,7 +14678,6 @@ intel_legacy_cursor_update(struct drm_plane *plane,
>   			   u32 src_w, u32 src_h,
>   			   struct drm_modeset_acquire_ctx *ctx)
>   {
> -	struct drm_i915_private *dev_priv = to_i915(crtc->dev);
>   	struct drm_plane_state *old_plane_state, *new_plane_state;
>   	struct intel_plane *intel_plane = to_intel_plane(plane);
>   	struct intel_crtc_state *crtc_state =
> @@ -14764,13 +14743,9 @@ intel_legacy_cursor_update(struct drm_plane *plane,
>   	if (ret)
>   		goto out_free;
>   
> -	ret = mutex_lock_interruptible(&dev_priv->drm.struct_mutex);
> -	if (ret)
> -		goto out_free;
> -
>   	ret = intel_plane_pin_fb(to_intel_plane_state(new_plane_state));
>   	if (ret)
> -		goto out_unlock;
> +		goto out_free;
>   
>   	intel_frontbuffer_flush(to_intel_frontbuffer(fb), ORIGIN_FLIP);
>   	intel_frontbuffer_track(to_intel_frontbuffer(old_plane_state->fb),
> @@ -14800,8 +14775,6 @@ intel_legacy_cursor_update(struct drm_plane *plane,
>   
>   	intel_plane_unpin_fb(to_intel_plane_state(old_plane_state));
>   
> -out_unlock:
> -	mutex_unlock(&dev_priv->drm.struct_mutex);
>   out_free:
>   	if (new_crtc_state)
>   		intel_crtc_destroy_state(crtc, &new_crtc_state->base);
> diff --git a/drivers/gpu/drm/i915/display/intel_fbdev.c b/drivers/gpu/drm/i915/display/intel_fbdev.c
> index d59eee5c5d9c..a3dea3f2dacd 100644
> --- a/drivers/gpu/drm/i915/display/intel_fbdev.c
> +++ b/drivers/gpu/drm/i915/display/intel_fbdev.c
> @@ -204,7 +204,6 @@ static int intelfb_create(struct drm_fb_helper *helper,
>   		sizes->fb_height = intel_fb->base.height;
>   	}
>   
> -	mutex_lock(&dev->struct_mutex);
>   	wakeref = intel_runtime_pm_get(&dev_priv->runtime_pm);
>   
>   	/* Pin the GGTT vma for our access via info->screen_base.
> @@ -266,7 +265,6 @@ static int intelfb_create(struct drm_fb_helper *helper,
>   	ifbdev->vma_flags = flags;
>   
>   	intel_runtime_pm_put(&dev_priv->runtime_pm, wakeref);
> -	mutex_unlock(&dev->struct_mutex);
>   	vga_switcheroo_client_fb_set(pdev, info);
>   	return 0;
>   
> @@ -274,7 +272,6 @@ static int intelfb_create(struct drm_fb_helper *helper,
>   	intel_unpin_fb_vma(vma, flags);
>   out_unlock:
>   	intel_runtime_pm_put(&dev_priv->runtime_pm, wakeref);
> -	mutex_unlock(&dev->struct_mutex);
>   	return ret;
>   }
>   
> @@ -291,11 +288,8 @@ static void intel_fbdev_destroy(struct intel_fbdev *ifbdev)
>   
>   	drm_fb_helper_fini(&ifbdev->helper);
>   
> -	if (ifbdev->vma) {
> -		mutex_lock(&ifbdev->helper.dev->struct_mutex);
> +	if (ifbdev->vma)
>   		intel_unpin_fb_vma(ifbdev->vma, ifbdev->vma_flags);
> -		mutex_unlock(&ifbdev->helper.dev->struct_mutex);
> -	}
>   
>   	if (ifbdev->fb)
>   		drm_framebuffer_remove(&ifbdev->fb->base);
> diff --git a/drivers/gpu/drm/i915/display/intel_overlay.c b/drivers/gpu/drm/i915/display/intel_overlay.c
> index 29edfc343716..4f36557b3f3b 100644
> --- a/drivers/gpu/drm/i915/display/intel_overlay.c
> +++ b/drivers/gpu/drm/i915/display/intel_overlay.c
> @@ -1303,15 +1303,11 @@ static int get_registers(struct intel_overlay *overlay, bool use_phys)
>   	struct i915_vma *vma;
>   	int err;
>   
> -	mutex_lock(&i915->drm.struct_mutex);
> -
>   	obj = i915_gem_object_create_stolen(i915, PAGE_SIZE);
>   	if (obj == NULL)
>   		obj = i915_gem_object_create_internal(i915, PAGE_SIZE);
> -	if (IS_ERR(obj)) {
> -		err = PTR_ERR(obj);
> -		goto err_unlock;
> -	}
> +	if (IS_ERR(obj))
> +		return PTR_ERR(obj);
>   
>   	vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0, PIN_MAPPABLE);
>   	if (IS_ERR(vma)) {
> @@ -1332,13 +1328,10 @@ static int get_registers(struct intel_overlay *overlay, bool use_phys)
>   	}
>   
>   	overlay->reg_bo = obj;
> -	mutex_unlock(&i915->drm.struct_mutex);
>   	return 0;
>   
>   err_put_bo:
>   	i915_gem_object_put(obj);
> -err_unlock:
> -	mutex_unlock(&i915->drm.struct_mutex);
>   	return err;
>   }
>   
> 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 f99920652751..9e72b42a86f5 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_client_blt.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_client_blt.c
> @@ -152,6 +152,17 @@ static void clear_pages_dma_fence_cb(struct dma_fence *fence,
>   	irq_work_queue(&w->irq_work);
>   }
>   
> +static int move_to_active(struct i915_vma *vma, struct i915_request *rq)
> +{
> +	int err;
> +
> +	err = i915_request_await_active(rq, &vma->active);
> +	if (err)
> +		return err;
> +
> +	return i915_active_ref(&vma->active, rq->timeline, rq);
> +}
> +
>   static void clear_pages_worker(struct work_struct *work)
>   {
>   	struct clear_pages_work *w = container_of(work, typeof(*w), work);
> @@ -211,7 +222,7 @@ static void clear_pages_worker(struct work_struct *work)
>   	 * keep track of the GPU activity within this vma/request, and
>   	 * propagate the signal from the request to w->dma.
>   	 */
> -	err = i915_active_ref(&vma->active, rq->timeline, rq);
> +	err = move_to_active(vma, rq);
>   	if (err)
>   		goto out_request;
>   
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context.c b/drivers/gpu/drm/i915/gem/i915_gem_context.c
> index f1c0e5d958f3..653f7275306a 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_context.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_context.c
> @@ -313,8 +313,6 @@ static void i915_gem_context_free(struct i915_gem_context *ctx)
>   	GEM_BUG_ON(!i915_gem_context_is_closed(ctx));
>   
>   	release_hw_id(ctx);
> -	if (ctx->vm)
> -		i915_vm_put(ctx->vm);
>   
>   	free_engines(rcu_access_pointer(ctx->engines));
>   	mutex_destroy(&ctx->engines_mutex);
> @@ -379,9 +377,13 @@ void i915_gem_context_release(struct kref *ref)
>   
>   static void context_close(struct i915_gem_context *ctx)
>   {
> +	i915_gem_context_set_closed(ctx);
> +
> +	if (ctx->vm)
> +		i915_vm_close(ctx->vm);
> +
>   	mutex_lock(&ctx->mutex);
>   
> -	i915_gem_context_set_closed(ctx);
>   	ctx->file_priv = ERR_PTR(-EBADF);
>   
>   	/*
> @@ -474,7 +476,7 @@ __set_ppgtt(struct i915_gem_context *ctx, struct i915_address_space *vm)
>   
>   	GEM_BUG_ON(old && i915_vm_is_4lvl(vm) != i915_vm_is_4lvl(old));
>   
> -	ctx->vm = i915_vm_get(vm);
> +	ctx->vm = i915_vm_open(vm);
>   	context_apply_all(ctx, __apply_ppgtt, vm);
>   
>   	return old;
> @@ -488,7 +490,7 @@ static void __assign_ppgtt(struct i915_gem_context *ctx,
>   
>   	vm = __set_ppgtt(ctx, vm);
>   	if (vm)
> -		i915_vm_put(vm);
> +		i915_vm_close(vm);
>   }
>   
>   static void __set_timeline(struct intel_timeline **dst,
> @@ -953,7 +955,7 @@ static int get_ppgtt(struct drm_i915_file_private *file_priv,
>   	if (ret < 0)
>   		goto err_unlock;
>   
> -	i915_vm_get(vm);
> +	i915_vm_open(vm);
>   
>   	args->size = 0;
>   	args->value = ret;
> @@ -973,7 +975,7 @@ static void set_ppgtt_barrier(void *data)
>   	if (INTEL_GEN(old->i915) < 8)
>   		gen6_ppgtt_unpin_all(i915_vm_to_ppgtt(old));
>   
> -	i915_vm_put(old);
> +	i915_vm_close(old);
>   }
>   
>   static int emit_ppgtt_update(struct i915_request *rq, void *data)
> @@ -1090,8 +1092,8 @@ static int set_ppgtt(struct drm_i915_file_private *file_priv,
>   				   set_ppgtt_barrier,
>   				   old);
>   	if (err) {
> -		i915_vm_put(__set_ppgtt(ctx, old));
> -		i915_vm_put(old);
> +		i915_vm_close(__set_ppgtt(ctx, old));
> +		i915_vm_close(old);
>   	}
>   
>   unlock:
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_domain.c b/drivers/gpu/drm/i915/gem/i915_gem_domain.c
> index f0c437b6e995..46a23409e1c0 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_domain.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_domain.c
> @@ -202,7 +202,11 @@ int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj,
>   		    i915_gem_valid_gtt_space(vma, cache_level))
>   			continue;
>   
> -		ret = i915_vma_unbind(vma);
> +		ret = mutex_lock_interruptible(&vma->vm->mutex);
> +		if (!ret) {
> +			ret = i915_vma_unbind(vma);
> +			mutex_unlock(&vma->vm->mutex);
> +		}
>   		if (ret)
>   			return ret;
>   
> @@ -288,7 +292,12 @@ int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj,
>   			if (!drm_mm_node_allocated(&vma->node))
>   				continue;
>   
> -			ret = i915_vma_bind(vma, cache_level, PIN_UPDATE);
> +			/* Wait for an earlier async bind */
> +			ret = i915_active_wait(&vma->active);
> +			if (ret)
> +				return ret;
> +
> +			ret = i915_vma_bind(vma, cache_level, PIN_UPDATE, NULL);
>   			if (ret)
>   				return ret;
>   		}
> @@ -389,16 +398,11 @@ int i915_gem_set_caching_ioctl(struct drm_device *dev, void *data,
>   	if (ret)
>   		goto out;
>   
> -	ret = mutex_lock_interruptible(&i915->drm.struct_mutex);
> -	if (ret)
> -		goto out;
> -
>   	ret = i915_gem_object_lock_interruptible(obj);
>   	if (ret == 0) {
>   		ret = i915_gem_object_set_cache_level(obj, level);
>   		i915_gem_object_unlock(obj);
>   	}
> -	mutex_unlock(&i915->drm.struct_mutex);
>   
>   out:
>   	i915_gem_object_put(obj);
> @@ -483,6 +487,7 @@ static void i915_gem_object_bump_inactive_ggtt(struct drm_i915_gem_object *obj)
>   		if (!drm_mm_node_allocated(&vma->node))
>   			continue;
>   
> +		GEM_BUG_ON(vma->vm != &i915->ggtt.vm);
>   		list_move_tail(&vma->vm_link, &vma->vm->bound_list);
>   	}
>   	mutex_unlock(&i915->ggtt.vm.mutex);
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> index c049199a1df5..c3dd8ce7e2b7 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
> @@ -550,8 +550,11 @@ eb_add_vma(struct i915_execbuffer *eb,
>   		eb_unreserve_vma(vma, vma->exec_flags);
>   
>   		list_add_tail(&vma->exec_link, &eb->unbound);
> -		if (drm_mm_node_allocated(&vma->node))
> +		if (drm_mm_node_allocated(&vma->node)) {
> +			mutex_lock(&vma->vm->mutex);
>   			err = i915_vma_unbind(vma);
> +			mutex_unlock(&vma->vm->mutex);
> +		}
>   		if (unlikely(err))
>   			vma->exec_flags = NULL;
>   	}
> @@ -698,7 +701,9 @@ static int eb_reserve(struct i915_execbuffer *eb)
>   
>   		case 1:
>   			/* Too fragmented, unbind everything and retry */
> +			mutex_lock(&eb->context->vm->mutex);
>   			err = i915_gem_evict_vm(eb->context->vm);
> +			mutex_unlock(&eb->context->vm->mutex);
>   			if (err)
>   				return err;
>   			break;
> @@ -972,7 +977,9 @@ static void reloc_cache_reset(struct reloc_cache *cache)
>   			ggtt->vm.clear_range(&ggtt->vm,
>   					     cache->node.start,
>   					     cache->node.size);
> +			mutex_lock(&ggtt->vm.mutex);
>   			drm_mm_remove_node(&cache->node);
> +			mutex_unlock(&ggtt->vm.mutex);
>   		} else {
>   			i915_vma_unpin((struct i915_vma *)cache->node.mm);
>   		}
> @@ -1047,11 +1054,13 @@ static void *reloc_iomap(struct drm_i915_gem_object *obj,
>   					       PIN_NOEVICT);
>   		if (IS_ERR(vma)) {
>   			memset(&cache->node, 0, sizeof(cache->node));
> +			mutex_lock(&ggtt->vm.mutex);
>   			err = drm_mm_insert_node_in_range
>   				(&ggtt->vm.mm, &cache->node,
>   				 PAGE_SIZE, 0, I915_COLOR_UNEVICTABLE,
>   				 0, ggtt->mappable_end,
>   				 DRM_MM_INSERT_LOW);
> +			mutex_unlock(&ggtt->vm.mutex);
>   			if (err) /* no inactive aperture space, use cpu reloc */
>   				return NULL;
>   		} else {
> @@ -1416,7 +1425,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, target->obj->cache_level,
> -					    PIN_GLOBAL);
> +					    PIN_GLOBAL, NULL);
>   			if (WARN_ONCE(err,
>   				      "Unexpected failure to bind target VMA!"))
>   				return err;
> @@ -2140,35 +2149,6 @@ static struct i915_request *eb_throttle(struct intel_context *ce)
>   	return i915_request_get(rq);
>   }
>   
> -static int
> -__eb_pin_context(struct i915_execbuffer *eb, struct intel_context *ce)
> -{
> -	int err;
> -
> -	if (likely(atomic_inc_not_zero(&ce->pin_count)))
> -		return 0;
> -
> -	err = mutex_lock_interruptible(&eb->i915->drm.struct_mutex);
> -	if (err)
> -		return err;
> -
> -	err = __intel_context_do_pin(ce);
> -	mutex_unlock(&eb->i915->drm.struct_mutex);
> -
> -	return err;
> -}
> -
> -static void
> -__eb_unpin_context(struct i915_execbuffer *eb, struct intel_context *ce)
> -{
> -	if (likely(atomic_add_unless(&ce->pin_count, -1, 1)))
> -		return;
> -
> -	mutex_lock(&eb->i915->drm.struct_mutex);
> -	intel_context_unpin(ce);
> -	mutex_unlock(&eb->i915->drm.struct_mutex);
> -}
> -
>   static int __eb_pin_engine(struct i915_execbuffer *eb, struct intel_context *ce)
>   {
>   	struct intel_timeline *tl;
> @@ -2188,7 +2168,7 @@ static int __eb_pin_engine(struct i915_execbuffer *eb, struct intel_context *ce)
>   	 * GGTT space, so do this first before we reserve a seqno for
>   	 * ourselves.
>   	 */
> -	err = __eb_pin_context(eb, ce);
> +	err = intel_context_pin(ce);
>   	if (err)
>   		return err;
>   
> @@ -2232,7 +2212,7 @@ static int __eb_pin_engine(struct i915_execbuffer *eb, struct intel_context *ce)
>   	intel_context_exit(ce);
>   	intel_context_timeline_unlock(tl);
>   err_unpin:
> -	__eb_unpin_context(eb, ce);
> +	intel_context_unpin(ce);
>   	return err;
>   }
>   
> @@ -2245,7 +2225,7 @@ static void eb_unpin_engine(struct i915_execbuffer *eb)
>   	intel_context_exit(ce);
>   	mutex_unlock(&tl->mutex);
>   
> -	__eb_unpin_context(eb, ce);
> +	intel_context_unpin(ce);
>   }
>   
>   static unsigned int
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_mman.c b/drivers/gpu/drm/i915/gem/i915_gem_mman.c
> index 82db2b783123..9a8c307c5aeb 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_mman.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_mman.c
> @@ -251,16 +251,6 @@ vm_fault_t i915_gem_fault(struct vm_fault *vmf)
>   		goto err_rpm;
>   	}
>   
> -	ret = i915_mutex_lock_interruptible(dev);
> -	if (ret)
> -		goto err_reset;
> -
> -	/* Access to snoopable pages through the GTT is incoherent. */
> -	if (obj->cache_level != I915_CACHE_NONE && !HAS_LLC(i915)) {
> -		ret = -EFAULT;
> -		goto err_unlock;
> -	}
> -
>   	/* Now pin it into the GTT as needed */
>   	vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0,
>   				       PIN_MAPPABLE |
> @@ -293,7 +283,13 @@ vm_fault_t i915_gem_fault(struct vm_fault *vmf)
>   	}
>   	if (IS_ERR(vma)) {
>   		ret = PTR_ERR(vma);
> -		goto err_unlock;
> +		goto err_reset;
> +	}
> +
> +	/* Access to snoopable pages through the GTT is incoherent. */
> +	if (obj->cache_level != I915_CACHE_NONE && !HAS_LLC(i915)) {
> +		ret = -EFAULT;
> +		goto err_unpin;
>   	}

Why have you moved this check to after pinning?

>   
>   	ret = i915_vma_pin_fence(vma);
> @@ -321,14 +317,12 @@ vm_fault_t i915_gem_fault(struct vm_fault *vmf)
>   		intel_wakeref_auto(&i915->ggtt.userfault_wakeref,
>   				   msecs_to_jiffies_timeout(CONFIG_DRM_I915_USERFAULT_AUTOSUSPEND));
>   
> -	i915_vma_set_ggtt_write(vma);
> -
> +	if (write)
> +		i915_vma_set_ggtt_write(vma);

Noise for what this patch is concerned?

>   err_fence:
>   	i915_vma_unpin_fence(vma);
>   err_unpin:
>   	__i915_vma_unpin(vma);
> -err_unlock:
> -	mutex_unlock(&dev->struct_mutex);
>   err_reset:
>   	intel_gt_reset_unlock(ggtt->vm.gt, srcu);
>   err_rpm:
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.c b/drivers/gpu/drm/i915/gem/i915_gem_object.c
> index 0ef60dae23a7..dbf9be9a79f4 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_object.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_object.c
> @@ -155,21 +155,30 @@ static void __i915_gem_free_objects(struct drm_i915_private *i915,
>   
>   	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
>   	llist_for_each_entry_safe(obj, on, freed, freed) {
> -		struct i915_vma *vma, *vn;
> -
>   		trace_i915_gem_object_destroy(obj);
>   
> -		mutex_lock(&i915->drm.struct_mutex);
> -
> -		list_for_each_entry_safe(vma, vn, &obj->vma.list, obj_link) {
> -			GEM_BUG_ON(i915_vma_is_active(vma));
> -			atomic_and(~I915_VMA_PIN_MASK, &vma->flags);
> -			i915_vma_destroy(vma);
> +		if (!list_empty(&obj->vma.list)) {
> +			struct i915_vma *vma;
> +
> +			/*
> +			 * Note that the vma keeps an object reference while
> +			 * it is active, so it *should* not sleep while we
> +			 * destroy it. Our debug code errs insits it *might*.
> +			 * For the moment, play along.
> +			 */
> +			spin_lock(&obj->vma.lock);
> +			while ((vma = list_first_entry_or_null(&obj->vma.list,
> +							       struct i915_vma,
> +							       obj_link))) 

What is the point of having a while loop inside top-level if !list_empty 
check? Looks theoretically racy, and even if that is irrelevant, it 
would be clearer to just do the while loop.

{
> +				GEM_BUG_ON(vma->obj != obj);
> +				spin_unlock(&obj->vma.lock);
> +
> +				i915_vma_destroy(vma);
> +
> +				spin_lock(&obj->vma.lock);
> +			}
> +			spin_unlock(&obj->vma.lock);
>   		}
> -		GEM_BUG_ON(!list_empty(&obj->vma.list));
> -		GEM_BUG_ON(!RB_EMPTY_ROOT(&obj->vma.tree));
> -
> -		mutex_unlock(&i915->drm.struct_mutex);
>   
>   		GEM_BUG_ON(atomic_read(&obj->bind_count));
>   		GEM_BUG_ON(obj->userfault_count);
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.h b/drivers/gpu/drm/i915/gem/i915_gem_object.h
> index 29b9eddc4c7f..a78af25dce36 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_object.h
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_object.h
> @@ -106,6 +106,11 @@ static inline void i915_gem_object_lock(struct drm_i915_gem_object *obj)
>   	dma_resv_lock(obj->base.resv, NULL);
>   }
>   
> +static inline bool i915_gem_object_trylock(struct drm_i915_gem_object *obj)
> +{
> +	return dma_resv_trylock(obj->base.resv);
> +}
> +
>   static inline int
>   i915_gem_object_lock_interruptible(struct drm_i915_gem_object *obj)
>   {
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_shrinker.c b/drivers/gpu/drm/i915/gem/i915_gem_shrinker.c
> index d2c05d752909..fd604ea12f7f 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_shrinker.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_shrinker.c
> @@ -16,40 +16,6 @@
>   
>   #include "i915_trace.h"
>   
> -static bool shrinker_lock(struct drm_i915_private *i915,
> -			  unsigned int flags,
> -			  bool *unlock)
> -{
> -	struct mutex *m = &i915->drm.struct_mutex;
> -
> -	switch (mutex_trylock_recursive(m)) {
> -	case MUTEX_TRYLOCK_RECURSIVE:
> -		*unlock = false;
> -		return true;
> -
> -	case MUTEX_TRYLOCK_FAILED:
> -		*unlock = false;
> -		if (flags & I915_SHRINK_ACTIVE &&
> -		    mutex_lock_killable_nested(m, I915_MM_SHRINKER) == 0)
> -			*unlock = true;
> -		return *unlock;
> -
> -	case MUTEX_TRYLOCK_SUCCESS:
> -		*unlock = true;
> -		return true;
> -	}
> -
> -	BUG();
> -}
> -
> -static void shrinker_unlock(struct drm_i915_private *i915, bool unlock)
> -{
> -	if (!unlock)
> -		return;
> -
> -	mutex_unlock(&i915->drm.struct_mutex);
> -}
> -
>   static bool swap_available(void)
>   {
>   	return get_nr_swap_pages() > 0;
> @@ -155,10 +121,6 @@ i915_gem_shrink(struct drm_i915_private *i915,
>   	intel_wakeref_t wakeref = 0;
>   	unsigned long count = 0;
>   	unsigned long scanned = 0;
> -	bool unlock;
> -
> -	if (!shrinker_lock(i915, shrink, &unlock))
> -		return 0;
>   
>   	/*
>   	 * When shrinking the active list, we should also consider active
> @@ -268,8 +230,6 @@ i915_gem_shrink(struct drm_i915_private *i915,
>   	if (shrink & I915_SHRINK_BOUND)
>   		intel_runtime_pm_put(&i915->runtime_pm, wakeref);
>   
> -	shrinker_unlock(i915, unlock);
> -
>   	if (nr_scanned)
>   		*nr_scanned += scanned;
>   	return count;
> @@ -339,19 +299,14 @@ i915_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc)
>   	struct drm_i915_private *i915 =
>   		container_of(shrinker, struct drm_i915_private, mm.shrinker);
>   	unsigned long freed;
> -	bool unlock;
>   
>   	sc->nr_scanned = 0;
>   
> -	if (!shrinker_lock(i915, 0, &unlock))
> -		return SHRINK_STOP;
> -
>   	freed = i915_gem_shrink(i915,
>   				sc->nr_to_scan,
>   				&sc->nr_scanned,
>   				I915_SHRINK_BOUND |
> -				I915_SHRINK_UNBOUND |
> -				I915_SHRINK_WRITEBACK);
> +				I915_SHRINK_UNBOUND);
>   	if (sc->nr_scanned < sc->nr_to_scan && current_is_kswapd()) {
>   		intel_wakeref_t wakeref;
>   
> @@ -366,8 +321,6 @@ i915_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc)
>   		}
>   	}
>   
> -	shrinker_unlock(i915, unlock);
> -
>   	return sc->nr_scanned ? freed : SHRINK_STOP;
>   }
>   
> @@ -384,6 +337,7 @@ i915_gem_shrinker_oom(struct notifier_block *nb, unsigned long event, void *ptr)
>   	freed_pages = 0;
>   	with_intel_runtime_pm(&i915->runtime_pm, wakeref)
>   		freed_pages += i915_gem_shrink(i915, -1UL, NULL,
> +					       I915_SHRINK_ACTIVE |
>   					       I915_SHRINK_BOUND |
>   					       I915_SHRINK_UNBOUND |
>   					       I915_SHRINK_WRITEBACK);
> @@ -419,10 +373,6 @@ i915_gem_shrinker_vmap(struct notifier_block *nb, unsigned long event, void *ptr
>   	struct i915_vma *vma, *next;
>   	unsigned long freed_pages = 0;
>   	intel_wakeref_t wakeref;
> -	bool unlock;
> -
> -	if (!shrinker_lock(i915, 0, &unlock))
> -		return NOTIFY_DONE;
>   
>   	with_intel_runtime_pm(&i915->runtime_pm, wakeref)
>   		freed_pages += i915_gem_shrink(i915, -1UL, NULL,
> @@ -439,15 +389,11 @@ i915_gem_shrinker_vmap(struct notifier_block *nb, unsigned long event, void *ptr
>   		if (!vma->iomap || i915_vma_is_active(vma))
>   			continue;
>   
> -		mutex_unlock(&i915->ggtt.vm.mutex);
>   		if (i915_vma_unbind(vma) == 0)
>   			freed_pages += count;
> -		mutex_lock(&i915->ggtt.vm.mutex);
>   	}
>   	mutex_unlock(&i915->ggtt.vm.mutex);
>   
> -	shrinker_unlock(i915, unlock);
> -
>   	*(unsigned long *)ptr += freed_pages;
>   	return NOTIFY_DONE;
>   }
> @@ -490,22 +436,9 @@ void i915_gem_shrinker_taints_mutex(struct drm_i915_private *i915,
>   
>   	fs_reclaim_acquire(GFP_KERNEL);
>   
> -	/*
> -	 * As we invariably rely on the struct_mutex within the shrinker,
> -	 * but have a complicated recursion dance, taint all the mutexes used
> -	 * within the shrinker with the struct_mutex. For completeness, we
> -	 * taint with all subclass of struct_mutex, even though we should
> -	 * only need tainting by I915_MM_NORMAL to catch possible ABBA
> -	 * deadlocks from using struct_mutex inside @mutex.
> -	 */
> -	mutex_acquire(&i915->drm.struct_mutex.dep_map,
> -		      I915_MM_SHRINKER, 0, _RET_IP_);
> -
>   	mutex_acquire(&mutex->dep_map, 0, 0, _RET_IP_);
>   	mutex_release(&mutex->dep_map, 0, _RET_IP_);
>   
> -	mutex_release(&i915->drm.struct_mutex.dep_map, 0, _RET_IP_);
> -
>   	fs_reclaim_release(GFP_KERNEL);
>   
>   	if (unlock)
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_stolen.c b/drivers/gpu/drm/i915/gem/i915_gem_stolen.c
> index 0d81de1461b4..d2aed728ad8d 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_stolen.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_stolen.c
> @@ -621,8 +621,6 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_i915_private *dev_priv
>   	if (!drm_mm_initialized(&dev_priv->mm.stolen))
>   		return NULL;
>   
> -	lockdep_assert_held(&dev_priv->drm.struct_mutex);
> -
>   	DRM_DEBUG_DRIVER("creating preallocated stolen object: stolen_offset=%pa, gtt_offset=%pa, size=%pa\n",
>   			 &stolen_offset, &gtt_offset, &size);
>   
> @@ -674,21 +672,25 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_i915_private *dev_priv
>   	 * setting up the GTT space. The actual reservation will occur
>   	 * later.
>   	 */
> +	mutex_lock(&ggtt->vm.mutex);
>   	ret = i915_gem_gtt_reserve(&ggtt->vm, &vma->node,
>   				   size, gtt_offset, obj->cache_level,
>   				   0);
>   	if (ret) {
>   		DRM_DEBUG_DRIVER("failed to allocate stolen GTT space\n");
> +		mutex_unlock(&ggtt->vm.mutex);
>   		goto err_pages;
>   	}
>   
>   	GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
>   
> +	GEM_BUG_ON(vma->pages);
>   	vma->pages = obj->mm.pages;
> +	atomic_set(&vma->pages_count, I915_VMA_PAGES_ACTIVE);

What is I915_VMA_PAGES_ACTIVE used for? Pinned? Has pages?

> +
>   	set_bit(I915_VMA_GLOBAL_BIND_BIT, __i915_vma_flags(vma));
>   	__i915_vma_set_map_and_fenceable(vma);
>   
> -	mutex_lock(&ggtt->vm.mutex);
>   	list_add_tail(&vma->vm_link, &ggtt->vm.bound_list);
>   	mutex_unlock(&ggtt->vm.mutex);
>   
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_tiling.c b/drivers/gpu/drm/i915/gem/i915_gem_tiling.c
> index ca0c2f451742..b9cfae0e4435 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_tiling.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_tiling.c
> @@ -181,22 +181,25 @@ static int
>   i915_gem_object_fence_prepare(struct drm_i915_gem_object *obj,
>   			      int tiling_mode, unsigned int stride)
>   {
> +	struct i915_ggtt *ggtt = &to_i915(obj->base.dev)->ggtt;
>   	struct i915_vma *vma;
> -	int ret;
> +	int ret = 0;
>   
>   	if (tiling_mode == I915_TILING_NONE)
>   		return 0;
>   
> +	mutex_lock(&ggtt->vm.mutex);
>   	for_each_ggtt_vma(vma, obj) {
>   		if (i915_vma_fence_prepare(vma, tiling_mode, stride))
>   			continue;

vma_fence_prepare doesn't need to be under mutex, but it's much easier 
for this loop, yes?

>   
>   		ret = i915_vma_unbind(vma);
>   		if (ret)
> -			return ret;
> +			break;
>   	}
> +	mutex_unlock(&ggtt->vm.mutex);
>   
> -	return 0;
> +	return ret;
>   }
>   
>   int
> @@ -212,7 +215,6 @@ i915_gem_object_set_tiling(struct drm_i915_gem_object *obj,
>   
>   	GEM_BUG_ON(!i915_tiling_ok(obj, tiling, stride));
>   	GEM_BUG_ON(!stride ^ (tiling == I915_TILING_NONE));
> -	lockdep_assert_held(&i915->drm.struct_mutex);
>   
>   	if ((tiling | stride) == obj->tiling_and_stride)
>   		return 0;
> @@ -364,12 +366,7 @@ i915_gem_set_tiling_ioctl(struct drm_device *dev, void *data,
>   		}
>   	}
>   
> -	err = mutex_lock_interruptible(&dev->struct_mutex);
> -	if (err)
> -		goto err;
> -
>   	err = i915_gem_object_set_tiling(obj, args->tiling_mode, args->stride);
> -	mutex_unlock(&dev->struct_mutex);
>   
>   	/* We have to maintain this existing ABI... */
>   	args->stride = i915_gem_object_get_stride(obj);
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_userptr.c b/drivers/gpu/drm/i915/gem/i915_gem_userptr.c
> index 74da35611d7c..cd36236e3faf 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_userptr.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_userptr.c
> @@ -92,7 +92,6 @@ userptr_mn_invalidate_range_start(struct mmu_notifier *_mn,
>   	struct i915_mmu_notifier *mn =
>   		container_of(_mn, struct i915_mmu_notifier, mn);
>   	struct interval_tree_node *it;
> -	struct mutex *unlock = NULL;
>   	unsigned long end;
>   	int ret = 0;
>   
> @@ -129,33 +128,13 @@ userptr_mn_invalidate_range_start(struct mmu_notifier *_mn,
>   		}
>   		spin_unlock(&mn->lock);
>   
> -		if (!unlock) {
> -			unlock = &mn->mm->i915->drm.struct_mutex;
> -
> -			switch (mutex_trylock_recursive(unlock)) {
> -			default:
> -			case MUTEX_TRYLOCK_FAILED:
> -				if (mutex_lock_killable_nested(unlock, I915_MM_SHRINKER)) {
> -					i915_gem_object_put(obj);
> -					return -EINTR;
> -				}
> -				/* fall through */
> -			case MUTEX_TRYLOCK_SUCCESS:
> -				break;
> -
> -			case MUTEX_TRYLOCK_RECURSIVE:
> -				unlock = ERR_PTR(-EEXIST);
> -				break;
> -			}
> -		}
> -
>   		ret = i915_gem_object_unbind(obj,
>   					     I915_GEM_OBJECT_UNBIND_ACTIVE);
>   		if (ret == 0)
>   			ret = __i915_gem_object_put_pages(obj, I915_MM_SHRINKER);
>   		i915_gem_object_put(obj);
>   		if (ret)
> -			goto unlock;
> +			return ret;
>   
>   		spin_lock(&mn->lock);
>   
> @@ -168,10 +147,6 @@ userptr_mn_invalidate_range_start(struct mmu_notifier *_mn,
>   	}
>   	spin_unlock(&mn->lock);
>   
> -unlock:
> -	if (!IS_ERR_OR_NULL(unlock))
> -		mutex_unlock(unlock);
> -
>   	return ret;
>   
>   }
> diff --git a/drivers/gpu/drm/i915/gem/selftests/huge_pages.c b/drivers/gpu/drm/i915/gem/selftests/huge_pages.c
> index c5cea4379216..cd771147b41f 100644
> --- a/drivers/gpu/drm/i915/gem/selftests/huge_pages.c
> +++ b/drivers/gpu/drm/i915/gem/selftests/huge_pages.c
> @@ -25,6 +25,17 @@ static const unsigned int page_sizes[] = {
>   	I915_GTT_PAGE_SIZE_4K,
>   };
>   
> +static int unlocked_vma_unbind(struct i915_vma *vma)
> +{
> +	int ret;
> +
> +	mutex_lock(&vma->vm->mutex);
> +	ret = i915_vma_unbind(vma);
> +	mutex_unlock(&vma->vm->mutex);
> +
> +	return ret;
> +}
> +
>   static unsigned int get_largest_page_size(struct drm_i915_private *i915,
>   					  u64 rem)
>   {
> @@ -333,7 +344,11 @@ static int igt_check_page_sizes(struct i915_vma *vma)
>   	struct drm_i915_private *i915 = vma->vm->i915;
>   	unsigned int supported = INTEL_INFO(i915)->page_sizes;
>   	struct drm_i915_gem_object *obj = vma->obj;
> -	int err = 0;
> +	int err;
> +
> +	err = i915_active_wait(&vma->active);
> +	if (err)
> +		return err;

Touched upon this in the previous email, but I think we need to put some 
clear notes in writing, in some good place, regarding when callers need 
to explicitly do this and when not. Don't know where though.. commit 
message is weak. Maybe kernel doc next to i915_vma_unbind?

>   
>   	if (!HAS_PAGE_SIZES(i915, vma->page_sizes.sg)) {
>   		pr_err("unsupported page_sizes.sg=%u, supported=%u\n",
> @@ -526,7 +541,7 @@ static int igt_mock_ppgtt_misaligned_dma(void *arg)
>   		 * pages.
>   		 */
>   		for (offset = 4096; offset < page_size; offset += 4096) {
> -			err = i915_vma_unbind(vma);
> +			err = unlocked_vma_unbind(vma);
>   			if (err) {
>   				i915_vma_close(vma);
>   				goto out_unpin;
> @@ -941,7 +956,7 @@ static int __igt_write_huge(struct intel_context *ce,
>   	if (IS_ERR(vma))
>   		return PTR_ERR(vma);
>   
> -	err = i915_vma_unbind(vma);
> +	err = unlocked_vma_unbind(vma);
>   	if (err)
>   		goto out_vma_close;
>   
> @@ -1390,7 +1405,7 @@ static int igt_ppgtt_pin_update(void *arg)
>   			goto out_unpin;
>   		}
>   
> -		err = i915_vma_bind(vma, I915_CACHE_NONE, PIN_UPDATE);
> +		err = i915_vma_bind(vma, I915_CACHE_NONE, PIN_UPDATE, NULL);
>   		if (err)
>   			goto out_unpin;
>   
> 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 da54a718c712..aa67c02ba98c 100644
> --- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c
> +++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c
> @@ -747,10 +747,7 @@ emit_rpcs_query(struct drm_i915_gem_object *obj,
>   	if (err)
>   		goto skip_request;
>   
> -	i915_vma_unpin(batch);
> -	i915_vma_close(batch);
> -	i915_vma_put(batch);
> -
> +	i915_vma_unpin_and_release(&batch, 0);

Is this consolidation enabled by this patch or was otherwise possible? 
Something rings familiar about some problem with 
i915_vma_unpin_and_release but I can't remember what.. Semantics are a 
bit weird though.. whereas pattern before was to i915_vma_put after 
instantiating a vma, now it is unpin_and_release.. release being a bit 
of an odd term in this space. There was/is no symmetry with get/put 
anyway so I guess it's fine.

>   	i915_vma_unpin(vma);
>   
>   	*rq_out = i915_request_get(rq);
> @@ -764,8 +761,7 @@ emit_rpcs_query(struct drm_i915_gem_object *obj,
>   err_request:
>   	i915_request_add(rq);
>   err_batch:
> -	i915_vma_unpin(batch);
> -	i915_vma_put(batch);
> +	i915_vma_unpin_and_release(&batch, 0);
>   err_vma:
>   	i915_vma_unpin(vma);
>   
> @@ -1309,9 +1305,7 @@ static int write_to_scratch(struct i915_gem_context *ctx,
>   	if (err)
>   		goto skip_request;
>   
> -	i915_vma_unpin(vma);
> -	i915_vma_close(vma);
> -	i915_vma_put(vma);
> +	i915_vma_unpin_and_release(&vma, 0);
>   
>   	i915_request_add(rq);
>   
> 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 1d27babff0ce..9c217dfe96a9 100644
> --- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c
> +++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c
> @@ -205,7 +205,6 @@ static int igt_partial_tiling(void *arg)
>   		goto out;
>   	}
>   
> -	mutex_lock(&i915->drm.struct_mutex);
>   	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
>   
>   	if (1) {
> @@ -318,7 +317,6 @@ next_tiling: ;
>   
>   out_unlock:
>   	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
> -	mutex_unlock(&i915->drm.struct_mutex);
>   	i915_gem_object_unpin_pages(obj);
>   out:
>   	i915_gem_object_put(obj);
> 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 ee5dc13a30b3..6718da20f35d 100644
> --- a/drivers/gpu/drm/i915/gem/selftests/igt_gem_utils.c
> +++ b/drivers/gpu/drm/i915/gem/selftests/igt_gem_utils.c
> @@ -154,9 +154,7 @@ int igt_gpu_fill_dw(struct intel_context *ce,
>   
>   	i915_request_add(rq);
>   
> -	i915_vma_unpin(batch);
> -	i915_vma_close(batch);
> -	i915_vma_put(batch);
> +	i915_vma_unpin_and_release(&batch, 0);
>   
>   	return 0;
>   
> @@ -165,7 +163,6 @@ int igt_gpu_fill_dw(struct intel_context *ce,
>   err_request:
>   	i915_request_add(rq);
>   err_batch:
> -	i915_vma_unpin(batch);
> -	i915_vma_put(batch);
> +	i915_vma_unpin_and_release(&batch, 0);
>   	return err;
>   }
> diff --git a/drivers/gpu/drm/i915/gt/intel_gt.c b/drivers/gpu/drm/i915/gt/intel_gt.c
> index d48ec9a76ed1..c2afffb94474 100644
> --- a/drivers/gpu/drm/i915/gt/intel_gt.c
> +++ b/drivers/gpu/drm/i915/gt/intel_gt.c
> @@ -207,11 +207,12 @@ void intel_gt_flush_ggtt_writes(struct intel_gt *gt)
>   
>   	with_intel_runtime_pm(&i915->runtime_pm, wakeref) {
>   		struct intel_uncore *uncore = gt->uncore;
> +		unsigned long flags;
>   
> -		spin_lock_irq(&uncore->lock);
> +		spin_lock_irqsave(&uncore->lock, flags);
>   		intel_uncore_posting_read_fw(uncore,
>   					     RING_HEAD(RENDER_RING_BASE));
> -		spin_unlock_irq(&uncore->lock);
> +		spin_unlock_irqrestore(&uncore->lock, flags);
>   	}
>   }
>   
> diff --git a/drivers/gpu/drm/i915/gt/intel_ringbuffer.c b/drivers/gpu/drm/i915/gt/intel_ringbuffer.c
> index ac55a0d054bd..855e97ccaf9f 100644
> --- a/drivers/gpu/drm/i915/gt/intel_ringbuffer.c
> +++ b/drivers/gpu/drm/i915/gt/intel_ringbuffer.c
> @@ -1336,15 +1336,13 @@ void intel_ring_free(struct kref *ref)
>   {
>   	struct intel_ring *ring = container_of(ref, typeof(*ring), ref);
>   
> -	i915_vma_close(ring->vma);
>   	i915_vma_put(ring->vma);
> -
>   	kfree(ring);
>   }
>   
>   static void __ring_context_fini(struct intel_context *ce)
>   {
> -	i915_gem_object_put(ce->state->obj);
> +	i915_vma_put(ce->state);
>   }
>   
>   static void ring_context_destroy(struct kref *ref)
> diff --git a/drivers/gpu/drm/i915/gt/selftest_hangcheck.c b/drivers/gpu/drm/i915/gt/selftest_hangcheck.c
> index a0098fc35921..e53eea1050f8 100644
> --- a/drivers/gpu/drm/i915/gt/selftest_hangcheck.c
> +++ b/drivers/gpu/drm/i915/gt/selftest_hangcheck.c
> @@ -1127,15 +1127,14 @@ static int evict_vma(void *data)
>   {
>   	struct evict_vma *arg = data;
>   	struct i915_address_space *vm = arg->vma->vm;
> -	struct drm_i915_private *i915 = vm->i915;
>   	struct drm_mm_node evict = arg->vma->node;
>   	int err;
>   
>   	complete(&arg->completion);
>   
> -	mutex_lock(&i915->drm.struct_mutex);
> +	mutex_lock(&vm->mutex);
>   	err = i915_gem_evict_for_node(vm, &evict, 0);
> -	mutex_unlock(&i915->drm.struct_mutex);
> +	mutex_unlock(&vm->mutex);
>   
>   	return err;
>   }
> @@ -1143,39 +1142,33 @@ static int evict_vma(void *data)
>   static int evict_fence(void *data)
>   {
>   	struct evict_vma *arg = data;
> -	struct drm_i915_private *i915 = arg->vma->vm->i915;
>   	int err;
>   
>   	complete(&arg->completion);
>   
> -	mutex_lock(&i915->drm.struct_mutex);
> -
>   	/* Mark the fence register as dirty to force the mmio update. */
>   	err = i915_gem_object_set_tiling(arg->vma->obj, I915_TILING_Y, 512);
>   	if (err) {
>   		pr_err("Invalid Y-tiling settings; err:%d\n", err);
> -		goto out_unlock;
> +		return err;
>   	}
>   
>   	err = i915_vma_pin(arg->vma, 0, 0, PIN_GLOBAL | PIN_MAPPABLE);
>   	if (err) {
>   		pr_err("Unable to pin vma for Y-tiled fence; err:%d\n", err);
> -		goto out_unlock;
> +		return err;
>   	}
>   
>   	err = i915_vma_pin_fence(arg->vma);
>   	i915_vma_unpin(arg->vma);
>   	if (err) {
>   		pr_err("Unable to pin Y-tiled fence; err:%d\n", err);
> -		goto out_unlock;
> +		return err;
>   	}
>   
>   	i915_vma_unpin_fence(arg->vma);
>   
> -out_unlock:
> -	mutex_unlock(&i915->drm.struct_mutex);
> -
> -	return err;
> +	return 0;
>   }
>   
>   static int __igt_reset_evict_vma(struct intel_gt *gt,
> diff --git a/drivers/gpu/drm/i915/gvt/aperture_gm.c b/drivers/gpu/drm/i915/gvt/aperture_gm.c
> index 5ff2437b2998..d996bbc7ea59 100644
> --- a/drivers/gpu/drm/i915/gvt/aperture_gm.c
> +++ b/drivers/gpu/drm/i915/gvt/aperture_gm.c
> @@ -61,14 +61,14 @@ static int alloc_gm(struct intel_vgpu *vgpu, bool high_gm)
>   		flags = PIN_MAPPABLE;
>   	}
>   
> -	mutex_lock(&dev_priv->drm.struct_mutex);
> +	mutex_lock(&dev_priv->ggtt.vm.mutex);
>   	mmio_hw_access_pre(dev_priv);
>   	ret = i915_gem_gtt_insert(&dev_priv->ggtt.vm, node,
>   				  size, I915_GTT_PAGE_SIZE,
>   				  I915_COLOR_UNEVICTABLE,
>   				  start, end, flags);
>   	mmio_hw_access_post(dev_priv);
> -	mutex_unlock(&dev_priv->drm.struct_mutex);
> +	mutex_unlock(&dev_priv->ggtt.vm.mutex);
>   	if (ret)
>   		gvt_err("fail to alloc %s gm space from host\n",
>   			high_gm ? "high" : "low");
> @@ -98,9 +98,9 @@ static int alloc_vgpu_gm(struct intel_vgpu *vgpu)
>   
>   	return 0;
>   out_free_aperture:
> -	mutex_lock(&dev_priv->drm.struct_mutex);
> +	mutex_lock(&dev_priv->ggtt.vm.mutex);
>   	drm_mm_remove_node(&vgpu->gm.low_gm_node);
> -	mutex_unlock(&dev_priv->drm.struct_mutex);
> +	mutex_unlock(&dev_priv->ggtt.vm.mutex);
>   	return ret;
>   }
>   
> @@ -108,10 +108,10 @@ static void free_vgpu_gm(struct intel_vgpu *vgpu)
>   {
>   	struct drm_i915_private *dev_priv = vgpu->gvt->dev_priv;
>   
> -	mutex_lock(&dev_priv->drm.struct_mutex);
> +	mutex_lock(&dev_priv->ggtt.vm.mutex);
>   	drm_mm_remove_node(&vgpu->gm.low_gm_node);
>   	drm_mm_remove_node(&vgpu->gm.high_gm_node);
> -	mutex_unlock(&dev_priv->drm.struct_mutex);
> +	mutex_unlock(&dev_priv->ggtt.vm.mutex);
>   }
>   
>   /**
> diff --git a/drivers/gpu/drm/i915/i915_active.c b/drivers/gpu/drm/i915/i915_active.c
> index 6a447f1d0110..6a37ed52957a 100644
> --- a/drivers/gpu/drm/i915/i915_active.c
> +++ b/drivers/gpu/drm/i915/i915_active.c
> @@ -146,6 +146,7 @@ __active_retire(struct i915_active *ref)
>   	if (!retire)
>   		return;
>   
> +	GEM_BUG_ON(rcu_access_pointer(ref->excl));
>   	rbtree_postorder_for_each_entry_safe(it, n, &root, node) {
>   		GEM_BUG_ON(i915_active_request_isset(&it->base));
>   		kmem_cache_free(global.slab_cache, it);
> @@ -245,6 +246,8 @@ void __i915_active_init(struct drm_i915_private *i915,
>   	ref->flags = 0;
>   	ref->active = active;
>   	ref->retire = retire;
> +
> +	ref->excl = NULL;
>   	ref->tree = RB_ROOT;
>   	ref->cache = NULL;
>   	init_llist_head(&ref->preallocated_barriers);
> @@ -341,6 +344,45 @@ int i915_active_ref(struct i915_active *ref,
>   	return err;
>   }
>   
> +static void excl_cb(struct dma_fence *f, struct dma_fence_cb *cb)
> +{
> +	struct i915_active *ref = container_of(cb, typeof(*ref), excl_cb);
> +
> +	RCU_INIT_POINTER(ref->excl, NULL);
> +	dma_fence_put(f);
> +
> +	active_retire(ref);
> +}
> +
> +void i915_active_set_exclusive(struct i915_active *ref, struct dma_fence *f)
> +{
> +	GEM_BUG_ON(i915_active_is_idle(ref));
> +
> +	dma_fence_get(f);
> +
> +	rcu_read_lock();
> +	if (rcu_access_pointer(ref->excl)) {
> +		struct dma_fence *old;
> +
> +		old = dma_fence_get_rcu_safe(&ref->excl);
> +		if (old) {
> +			if (dma_fence_remove_callback(old, &ref->excl_cb))
> +				atomic_dec(&ref->count);
> +			dma_fence_put(old);
> +		}

Put some commentary in describing the business done with ref->excl and 
callbacks. Or this goes away later?

> +	}
> +	rcu_read_unlock();
> +
> +	atomic_inc(&ref->count);
> +	rcu_assign_pointer(ref->excl, f);
> +
> +	if (dma_fence_add_callback(f, &ref->excl_cb, excl_cb)) {
> +		RCU_INIT_POINTER(ref->excl, NULL);
> +		atomic_dec(&ref->count);
> +		dma_fence_put(f);
> +	}
> +}
> +
>   int i915_active_acquire(struct i915_active *ref)
>   {
>   	int err;
> @@ -399,6 +441,25 @@ void i915_active_ungrab(struct i915_active *ref)
>   	__active_ungrab(ref);
>   }
>   
> +static int excl_wait(struct i915_active *ref)
> +{
> +	struct dma_fence *old;
> +	int err = 0;
> +
> +	if (!rcu_access_pointer(ref->excl))
> +		return 0;
> +
> +	rcu_read_lock();
> +	old = dma_fence_get_rcu_safe(&ref->excl);
> +	rcu_read_unlock();

ref->excl can go something to NULL but not NULL to something in here?

> +	if (old) {
> +		err = dma_fence_wait(old, true);
> +		dma_fence_put(old);
> +	}
> +
> +	return err;
> +}
> +
>   int i915_active_wait(struct i915_active *ref)
>   {
>   	struct active_node *it, *n;
> @@ -419,6 +480,10 @@ int i915_active_wait(struct i915_active *ref)
>   		return 0;
>   	}
>   
> +	err = excl_wait(ref);
> +	if (err)
> +		goto out;
> +
>   	rbtree_postorder_for_each_entry_safe(it, n, &ref->tree, node) {
>   		if (is_barrier(&it->base)) { /* unconnected idle-barrier */
>   			err = -EBUSY;
> @@ -430,6 +495,7 @@ int i915_active_wait(struct i915_active *ref)
>   			break;
>   	}
>   
> +out:
>   	__active_retire(ref);
>   	if (err)
>   		return err;
> @@ -454,26 +520,22 @@ int i915_request_await_active_request(struct i915_request *rq,
>   
>   int i915_request_await_active(struct i915_request *rq, struct i915_active *ref)
>   {
> -	struct active_node *it, *n;
> -	int err;
> -
> -	if (RB_EMPTY_ROOT(&ref->tree))
> -		return 0;
> +	int err = 0;
>   
> -	/* await allocates and so we need to avoid hitting the shrinker */
> -	err = i915_active_acquire(ref);
> -	if (err)
> -		return err;
> +	if (rcu_access_pointer(ref->excl)) {

excl_wait used if-not-return pattern, but essentialy the same question 
about possible races. I guess there aren't any since only retirement is 
relevant which is something-to-NULL and that's handled fine. But I am 
not sure, possibly comments are in order.

> +		struct dma_fence *fence;
>   
> -	mutex_lock(&ref->mutex);
> -	rbtree_postorder_for_each_entry_safe(it, n, &ref->tree, node) {
> -		err = i915_request_await_active_request(rq, &it->base);
> -		if (err)
> -			break;
> +		rcu_read_lock();
> +		fence = dma_fence_get_rcu_safe(&ref->excl);
> +		rcu_read_unlock();
> +		if (fence) {
> +			err = i915_request_await_dma_fence(rq, fence);
> +			dma_fence_put(fence);
> +		}
>   	}
> -	mutex_unlock(&ref->mutex);
>   
> -	i915_active_release(ref);
> +	/* In the future we may choose to await on all fences */
> +
>   	return err;
>   }
>   
> diff --git a/drivers/gpu/drm/i915/i915_active.h b/drivers/gpu/drm/i915/i915_active.h
> index f95058f99057..af3d536e26fd 100644
> --- a/drivers/gpu/drm/i915/i915_active.h
> +++ b/drivers/gpu/drm/i915/i915_active.h
> @@ -373,6 +373,13 @@ int i915_active_ref(struct i915_active *ref,
>   		    struct intel_timeline *tl,
>   		    struct i915_request *rq);
>   
> +void i915_active_set_exclusive(struct i915_active *ref, struct dma_fence *f);
> +
> +static inline bool i915_active_has_exclusive(struct i915_active *ref)
> +{
> +	return rcu_access_pointer(ref->excl);
> +}
> +
>   int i915_active_wait(struct i915_active *ref);
>   
>   int i915_request_await_active(struct i915_request *rq,
> diff --git a/drivers/gpu/drm/i915/i915_active_types.h b/drivers/gpu/drm/i915/i915_active_types.h
> index 1854e7d168c1..86e7a232ea3c 100644
> --- a/drivers/gpu/drm/i915/i915_active_types.h
> +++ b/drivers/gpu/drm/i915/i915_active_types.h
> @@ -8,6 +8,7 @@
>   #define _I915_ACTIVE_TYPES_H_
>   
>   #include <linux/atomic.h>
> +#include <linux/dma-fence.h>
>   #include <linux/llist.h>
>   #include <linux/mutex.h>
>   #include <linux/rbtree.h>
> @@ -51,6 +52,10 @@ struct i915_active {
>   	struct mutex mutex;
>   	atomic_t count;
>   
> +	/* Preallocated "exclusive" node */
> +	struct dma_fence __rcu *excl;
> +	struct dma_fence_cb excl_cb;
> +
>   	unsigned long flags;
>   #define I915_ACTIVE_GRAB_BIT 0
>   
> diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
> index 814f62fca727..3eed2efa8d13 100644
> --- a/drivers/gpu/drm/i915/i915_gem.c
> +++ b/drivers/gpu/drm/i915/i915_gem.c
> @@ -62,20 +62,31 @@
>   #include "intel_pm.h"
>   
>   static int
> -insert_mappable_node(struct i915_ggtt *ggtt,
> -                     struct drm_mm_node *node, u32 size)
> +insert_mappable_node(struct i915_ggtt *ggtt, struct drm_mm_node *node, u32 size)
>   {
> +	int err;
> +
> +	err = mutex_lock_interruptible(&ggtt->vm.mutex);
> +	if (err)
> +		return err;
> +
>   	memset(node, 0, sizeof(*node));
> -	return drm_mm_insert_node_in_range(&ggtt->vm.mm, node,
> -					   size, 0, I915_COLOR_UNEVICTABLE,
> -					   0, ggtt->mappable_end,
> -					   DRM_MM_INSERT_LOW);
> +	err = drm_mm_insert_node_in_range(&ggtt->vm.mm, node,
> +					  size, 0, I915_COLOR_UNEVICTABLE,
> +					  0, ggtt->mappable_end,
> +					  DRM_MM_INSERT_LOW);
> +
> +	mutex_unlock(&ggtt->vm.mutex);
> +
> +	return err;
>   }
>   
>   static void
> -remove_mappable_node(struct drm_mm_node *node)
> +remove_mappable_node(struct i915_ggtt *ggtt, struct drm_mm_node *node)
>   {
> +	mutex_lock(&ggtt->vm.mutex);
>   	drm_mm_remove_node(node);
> +	mutex_unlock(&ggtt->vm.mutex);
>   }
>   
>   int
> @@ -87,7 +98,8 @@ i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data,
>   	struct i915_vma *vma;
>   	u64 pinned;
>   
> -	mutex_lock(&ggtt->vm.mutex);
> +	if (mutex_lock_interruptible(&ggtt->vm.mutex))
> +		return -EINTR;
>   
>   	pinned = ggtt->vm.reserved;
>   	list_for_each_entry(vma, &ggtt->vm.bound_list, vm_link)
> @@ -109,20 +121,31 @@ int i915_gem_object_unbind(struct drm_i915_gem_object *obj,
>   	LIST_HEAD(still_in_list);
>   	int ret = 0;
>   
> -	lockdep_assert_held(&obj->base.dev->struct_mutex);
> -
>   	spin_lock(&obj->vma.lock);
>   	while (!ret && (vma = list_first_entry_or_null(&obj->vma.list,
>   						       struct i915_vma,
>   						       obj_link))) {
> +		struct i915_address_space *vm = vma->vm;
> +
> +		ret = -EBUSY;
> +		if (!i915_vm_tryopen(vm))
> +			break;

This is some race between different paths of vm closing/destruction and 
vma cleanup? We need a comment about it somewhere.. Here I guess is a 
good place.

And why break and not continue, there will be a second call coming from 
somewhere when it fails?

> +
>   		list_move_tail(&vma->obj_link, &still_in_list);
>   		spin_unlock(&obj->vma.lock);
>   
> -		ret = -EBUSY;
>   		if (flags & I915_GEM_OBJECT_UNBIND_ACTIVE ||
> -		    !i915_vma_is_active(vma))
> -			ret = i915_vma_unbind(vma);
> +		    !i915_vma_is_active(vma)) {
> +			struct i915_address_space *vm = vma->vm;
> +
> +			ret = mutex_lock_interruptible(&vm->mutex);
> +			if (!ret) {
> +				ret = i915_vma_unbind(vma);
> +				mutex_unlock(&vm->mutex);
> +			}
> +		}
>   
> +		i915_vm_close(vm);
>   		spin_lock(&obj->vma.lock);
>   	}
>   	list_splice(&still_in_list, &obj->vma.list);
> @@ -338,10 +361,6 @@ i915_gem_gtt_pread(struct drm_i915_gem_object *obj,
>   	u64 remain, offset;
>   	int ret;
>   
> -	ret = mutex_lock_interruptible(&i915->drm.struct_mutex);
> -	if (ret)
> -		return ret;
> -
>   	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
>   	vma = ERR_PTR(-ENODEV);
>   	if (!i915_gem_object_is_tiled(obj))
> @@ -355,12 +374,10 @@ i915_gem_gtt_pread(struct drm_i915_gem_object *obj,
>   	} else {
>   		ret = insert_mappable_node(ggtt, &node, PAGE_SIZE);
>   		if (ret)
> -			goto out_unlock;
> +			goto out_rpm;
>   		GEM_BUG_ON(!drm_mm_node_allocated(&node));
>   	}
>   
> -	mutex_unlock(&i915->drm.struct_mutex);
> -
>   	ret = i915_gem_object_lock_interruptible(obj);
>   	if (ret)
>   		goto out_unpin;
> @@ -414,17 +431,14 @@ i915_gem_gtt_pread(struct drm_i915_gem_object *obj,
>   
>   	i915_gem_object_unlock_fence(obj, fence);
>   out_unpin:
> -	mutex_lock(&i915->drm.struct_mutex);
>   	if (drm_mm_node_allocated(&node)) {
>   		ggtt->vm.clear_range(&ggtt->vm, node.start, node.size);
> -		remove_mappable_node(&node);
> +		remove_mappable_node(ggtt, &node);
>   	} else {
>   		i915_vma_unpin(vma);
>   	}
> -out_unlock:
> +out_rpm:
>   	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
> -	mutex_unlock(&i915->drm.struct_mutex);
> -
>   	return ret;
>   }
>   
> @@ -531,10 +545,6 @@ i915_gem_gtt_pwrite_fast(struct drm_i915_gem_object *obj,
>   	void __user *user_data;
>   	int ret;
>   
> -	ret = mutex_lock_interruptible(&i915->drm.struct_mutex);
> -	if (ret)
> -		return ret;
> -
>   	if (i915_gem_object_has_struct_page(obj)) {
>   		/*
>   		 * Avoid waking the device up if we can fallback, as
> @@ -544,10 +554,8 @@ i915_gem_gtt_pwrite_fast(struct drm_i915_gem_object *obj,
>   		 * using the cache bypass of indirect GGTT access.
>   		 */
>   		wakeref = intel_runtime_pm_get_if_in_use(rpm);
> -		if (!wakeref) {
> -			ret = -EFAULT;
> -			goto out_unlock;
> -		}
> +		if (!wakeref)
> +			return -EFAULT;
>   	} else {
>   		/* No backing pages, no fallback, we must force GGTT access */
>   		wakeref = intel_runtime_pm_get(rpm);
> @@ -569,8 +577,6 @@ i915_gem_gtt_pwrite_fast(struct drm_i915_gem_object *obj,
>   		GEM_BUG_ON(!drm_mm_node_allocated(&node));
>   	}
>   
> -	mutex_unlock(&i915->drm.struct_mutex);
> -
>   	ret = i915_gem_object_lock_interruptible(obj);
>   	if (ret)
>   		goto out_unpin;
> @@ -634,18 +640,15 @@ i915_gem_gtt_pwrite_fast(struct drm_i915_gem_object *obj,
>   
>   	i915_gem_object_unlock_fence(obj, fence);
>   out_unpin:
> -	mutex_lock(&i915->drm.struct_mutex);
>   	intel_gt_flush_ggtt_writes(ggtt->vm.gt);
>   	if (drm_mm_node_allocated(&node)) {
>   		ggtt->vm.clear_range(&ggtt->vm, node.start, node.size);
> -		remove_mappable_node(&node);
> +		remove_mappable_node(ggtt, &node);
>   	} else {
>   		i915_vma_unpin(vma);
>   	}
>   out_rpm:
>   	intel_runtime_pm_put(rpm, wakeref);
> -out_unlock:
> -	mutex_unlock(&i915->drm.struct_mutex);
>   	return ret;
>   }
>   
> @@ -967,8 +970,6 @@ i915_gem_object_ggtt_pin(struct drm_i915_gem_object *obj,
>   	struct i915_vma *vma;
>   	int ret;
>   
> -	lockdep_assert_held(&obj->base.dev->struct_mutex);
> -
>   	if (flags & PIN_MAPPABLE &&
>   	    (!view || view->type == I915_GGTT_VIEW_NORMAL)) {
>   		/* If the required space is larger than the available
> @@ -1015,14 +1016,9 @@ i915_gem_object_ggtt_pin(struct drm_i915_gem_object *obj,
>   				return ERR_PTR(-ENOSPC);
>   		}
>   
> -		WARN(i915_vma_is_pinned(vma),
> -		     "bo is already pinned in ggtt with incorrect alignment:"
> -		     " offset=%08x, req.alignment=%llx,"
> -		     " req.map_and_fenceable=%d, vma->map_and_fenceable=%d\n",
> -		     i915_ggtt_offset(vma), alignment,
> -		     !!(flags & PIN_MAPPABLE),
> -		     i915_vma_is_map_and_fenceable(vma));

Why removing the WARN?

> +		mutex_lock(&vma->vm->mutex);
>   		ret = i915_vma_unbind(vma);
> +		mutex_unlock(&vma->vm->mutex);
>   		if (ret)
>   			return ERR_PTR(ret);
>   	}
> @@ -1328,7 +1324,9 @@ static int __intel_engines_record_defaults(struct drm_i915_private *i915)
>   		 * from the GTT to prevent such accidents and reclaim the
>   		 * space.
>   		 */
> +		mutex_lock(&state->vm->mutex);
>   		err = i915_vma_unbind(state);
> +		mutex_unlock(&state->vm->mutex);
>   		if (err)
>   			goto out;
>   
> diff --git a/drivers/gpu/drm/i915/i915_gem_evict.c b/drivers/gpu/drm/i915/i915_gem_evict.c
> index 7abcac3b5e2e..44f5b638fa43 100644
> --- a/drivers/gpu/drm/i915/i915_gem_evict.c
> +++ b/drivers/gpu/drm/i915/i915_gem_evict.c
> @@ -47,8 +47,7 @@ static int ggtt_flush(struct drm_i915_private *i915)
>   	 * bound by their active reference.
>   	 */
>   	return i915_gem_wait_for_idle(i915,
> -				      I915_WAIT_INTERRUPTIBLE |
> -				      I915_WAIT_LOCKED,
> +				      I915_WAIT_INTERRUPTIBLE,
>   				      MAX_SCHEDULE_TIMEOUT);
>   }
>   
> @@ -104,7 +103,7 @@ i915_gem_evict_something(struct i915_address_space *vm,
>   	struct i915_vma *active;
>   	int ret;
>   
> -	lockdep_assert_held(&vm->i915->drm.struct_mutex);
> +	lockdep_assert_held(&vm->mutex);
>   	trace_i915_gem_evict(vm, min_size, alignment, flags);
>   
>   	/*
> @@ -127,15 +126,6 @@ i915_gem_evict_something(struct i915_address_space *vm,
>   				    min_size, alignment, cache_level,
>   				    start, end, mode);
>   
> -	/*
> -	 * Retire before we search the active list. Although we have
> -	 * reasonable accuracy in our retirement lists, we may have
> -	 * a stray pin (preventing eviction) that can only be resolved by
> -	 * retiring.
> -	 */
> -	if (!(flags & PIN_NONBLOCK))
> -		i915_retire_requests(dev_priv);

Not needed any more because?

> -
>   search_again:
>   	active = NULL;
>   	INIT_LIST_HEAD(&eviction_list);
> @@ -269,7 +259,7 @@ int i915_gem_evict_for_node(struct i915_address_space *vm,
>   	bool check_color;
>   	int ret = 0;
>   
> -	lockdep_assert_held(&vm->i915->drm.struct_mutex);
> +	lockdep_assert_held(&vm->mutex);
>   	GEM_BUG_ON(!IS_ALIGNED(start, I915_GTT_PAGE_SIZE));
>   	GEM_BUG_ON(!IS_ALIGNED(end, I915_GTT_PAGE_SIZE));
>   
> @@ -375,7 +365,7 @@ int i915_gem_evict_vm(struct i915_address_space *vm)
>   	struct i915_vma *vma, *next;
>   	int ret;
>   
> -	lockdep_assert_held(&vm->i915->drm.struct_mutex);
> +	lockdep_assert_held(&vm->mutex);
>   	trace_i915_gem_evict_vm(vm);
>   
>   	/* Switch back to the default context in order to unpin
> @@ -390,7 +380,6 @@ int i915_gem_evict_vm(struct i915_address_space *vm)
>   	}
>   
>   	INIT_LIST_HEAD(&eviction_list);
> -	mutex_lock(&vm->mutex);
>   	list_for_each_entry(vma, &vm->bound_list, vm_link) {
>   		if (i915_vma_is_pinned(vma))
>   			continue;
> @@ -398,7 +387,6 @@ int i915_gem_evict_vm(struct i915_address_space *vm)
>   		__i915_vma_pin(vma);
>   		list_add(&vma->evict_link, &eviction_list);
>   	}
> -	mutex_unlock(&vm->mutex);
>   
>   	ret = 0;
>   	list_for_each_entry_safe(vma, next, &eviction_list, evict_link) {
> diff --git a/drivers/gpu/drm/i915/i915_gem_fence_reg.c b/drivers/gpu/drm/i915/i915_gem_fence_reg.c
> index 615a9f4ef30c..414d839668d7 100644
> --- a/drivers/gpu/drm/i915/i915_gem_fence_reg.c
> +++ b/drivers/gpu/drm/i915/i915_gem_fence_reg.c
> @@ -237,6 +237,7 @@ static int fence_update(struct i915_fence_reg *fence,
>   
>   	old = xchg(&fence->vma, NULL);
>   	if (old) {
> +		/* XXX Ideally we would move the waiting to outside the mutex */
>   		ret = i915_active_wait(&old->active);
>   		if (ret) {
>   			fence->vma = old;
> @@ -331,13 +332,15 @@ static struct i915_fence_reg *fence_find(struct drm_i915_private *i915)
>   	return ERR_PTR(-EDEADLK);
>   }
>   
> -static int __i915_vma_pin_fence(struct i915_vma *vma)
> +int __i915_vma_pin_fence(struct i915_vma *vma)
>   {
>   	struct i915_ggtt *ggtt = i915_vm_to_ggtt(vma->vm);
>   	struct i915_fence_reg *fence;
>   	struct i915_vma *set = i915_gem_object_is_tiled(vma->obj) ? vma : NULL;
>   	int err;
>   
> +	lockdep_assert_held(&vma->vm->mutex);
> +
>   	/* Just update our place in the LRU if our fence is getting reused. */
>   	if (vma->fence) {
>   		fence = vma->fence;
> diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
> index 56d27cf09a3d..b2e4827788fc 100644
> --- a/drivers/gpu/drm/i915/i915_gem_gtt.c
> +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
> @@ -150,16 +150,18 @@ static void gmch_ggtt_invalidate(struct i915_ggtt *ggtt)
>   
>   static int ppgtt_bind_vma(struct i915_vma *vma,
>   			  enum i915_cache_level cache_level,
> -			  u32 unused)
> +			  u32 flags)
>   {
>   	u32 pte_flags;
>   	int err;
>   
> -	if (!i915_vma_is_bound(vma, I915_VMA_LOCAL_BIND)) {
> +	if (flags & I915_VMA_ALLOC) {
>   		err = vma->vm->allocate_va_range(vma->vm,
>   						 vma->node.start, vma->size);
>   		if (err)
>   			return err;
> +
> +		set_bit(I915_VMA_ALLOC_BIT, __i915_vma_flags(vma));
>   	}
>   
>   	/* Applicable to VLV, and gen8+ */
> @@ -167,6 +169,7 @@ static int ppgtt_bind_vma(struct i915_vma *vma,
>   	if (i915_gem_object_is_readonly(vma->obj))
>   		pte_flags |= PTE_READ_ONLY;
>   
> +	GEM_BUG_ON(!test_bit(I915_VMA_ALLOC_BIT, __i915_vma_flags(vma)));
>   	vma->vm->insert_entries(vma->vm, vma, cache_level, pte_flags);
>   	wmb();
>   
> @@ -175,7 +178,8 @@ static int ppgtt_bind_vma(struct i915_vma *vma,
>   
>   static void ppgtt_unbind_vma(struct i915_vma *vma)
>   {
> -	vma->vm->clear_range(vma->vm, vma->node.start, vma->size);
> +	if (test_and_clear_bit(I915_VMA_ALLOC_BIT, __i915_vma_flags(vma)))
> +		vma->vm->clear_range(vma->vm, vma->node.start, vma->size);

VMA bound and allocated are now synonyms?

>   }
>   
>   static int ppgtt_set_pages(struct i915_vma *vma)
> @@ -503,15 +507,25 @@ static void i915_address_space_fini(struct i915_address_space *vm)
>   	mutex_destroy(&vm->mutex);
>   }
>   
> -static void ppgtt_destroy_vma(struct i915_address_space *vm)
> +void __i915_vm_close(struct i915_address_space *vm)
>   {
>   	struct i915_vma *vma, *vn;
>   
> -	mutex_lock(&vm->i915->drm.struct_mutex);
> -	list_for_each_entry_safe(vma, vn, &vm->bound_list, vm_link)
> +	mutex_lock(&vm->mutex);
> +	list_for_each_entry_safe(vma, vn, &vm->bound_list, vm_link) {
> +		struct drm_i915_gem_object *obj = vma->obj;
> +
> +		if (!kref_get_unless_zero(&obj->base.refcount))
> +			continue;
> +
> +		atomic_and(~I915_VMA_PIN_MASK, &vma->flags);
> +		WARN_ON(i915_vma_unbind(vma));
>   		i915_vma_destroy(vma);
> +
> +		i915_gem_object_put(obj);
> +	}
>   	GEM_BUG_ON(!list_empty(&vm->bound_list));
> -	mutex_unlock(&vm->i915->drm.struct_mutex);
> +	mutex_unlock(&vm->mutex);
>   }
>   
>   static void __i915_vm_release(struct work_struct *work)
> @@ -519,8 +533,6 @@ static void __i915_vm_release(struct work_struct *work)
>   	struct i915_address_space *vm =
>   		container_of(work, struct i915_address_space, rcu.work);
>   
> -	ppgtt_destroy_vma(vm);
> -
>   	vm->cleanup(vm);
>   	i915_address_space_fini(vm);
>   
> @@ -535,7 +547,6 @@ void i915_vm_release(struct kref *kref)
>   	GEM_BUG_ON(i915_is_ggtt(vm));
>   	trace_i915_ppgtt_release(vm);
>   
> -	vm->closed = true;
>   	queue_rcu_work(vm->i915->wq, &vm->rcu);
>   }
>   
> @@ -543,6 +554,7 @@ static 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);
>   
>   	/*
>   	 * The vm->mutex must be reclaim safe (for use in the shrinker).
> @@ -1769,12 +1781,8 @@ static void gen6_ppgtt_free_pd(struct gen6_ppgtt *ppgtt)
>   static void gen6_ppgtt_cleanup(struct i915_address_space *vm)
>   {
>   	struct gen6_ppgtt *ppgtt = to_gen6_ppgtt(i915_vm_to_ppgtt(vm));
> -	struct drm_i915_private *i915 = vm->i915;
>   
> -	/* FIXME remove the struct_mutex to bring the locking under control */
> -	mutex_lock(&i915->drm.struct_mutex);
>   	i915_vma_destroy(ppgtt->vma);
> -	mutex_unlock(&i915->drm.struct_mutex);
>   
>   	gen6_ppgtt_free_pd(ppgtt);
>   	free_scratch(vm);
> @@ -1863,7 +1871,8 @@ static struct i915_vma *pd_vma_create(struct gen6_ppgtt *ppgtt, int size)
>   
>   	i915_active_init(i915, &vma->active, NULL, NULL);
>   
> -	vma->vm = &ggtt->vm;
> +	mutex_init(&vma->pages_mutex);
> +	vma->vm = i915_vm_get(&ggtt->vm);
>   	vma->ops = &pd_vma_ops;
>   	vma->private = ppgtt;
>   
> @@ -1883,7 +1892,7 @@ int gen6_ppgtt_pin(struct i915_ppgtt *base)
>   	struct gen6_ppgtt *ppgtt = to_gen6_ppgtt(base);
>   	int err = 0;
>   
> -	GEM_BUG_ON(ppgtt->base.vm.closed);
> +	GEM_BUG_ON(!atomic_read(&ppgtt->base.vm.open));
>   
>   	/*
>   	 * Workaround the limited maximum vma->pin_count and the aliasing_ppgtt
> @@ -2460,14 +2469,18 @@ static int aliasing_gtt_bind_vma(struct i915_vma *vma,
>   	if (flags & I915_VMA_LOCAL_BIND) {
>   		struct i915_ppgtt *alias = i915_vm_to_ggtt(vma->vm)->alias;
>   
> -		if (!i915_vma_is_bound(vma, I915_VMA_LOCAL_BIND)) {
> +		if (flags & I915_VMA_ALLOC) {
>   			ret = alias->vm.allocate_va_range(&alias->vm,
>   							  vma->node.start,
>   							  vma->size);
>   			if (ret)
>   				return ret;
> +
> +			set_bit(I915_VMA_ALLOC_BIT, __i915_vma_flags(vma));
>   		}
>   
> +		GEM_BUG_ON(!test_bit(I915_VMA_ALLOC_BIT,
> +				     __i915_vma_flags(vma)));
>   		alias->vm.insert_entries(&alias->vm, vma,
>   					 cache_level, pte_flags);
>   	}
> @@ -2496,7 +2509,7 @@ static void aliasing_gtt_unbind_vma(struct i915_vma *vma)
>   			vm->clear_range(vm, vma->node.start, vma->size);
>   	}
>   
> -	if (i915_vma_is_bound(vma, I915_VMA_LOCAL_BIND)) {
> +	if (test_and_clear_bit(I915_VMA_ALLOC_BIT, __i915_vma_flags(vma))) {
>   		struct i915_address_space *vm =
>   			&i915_vm_to_ggtt(vma->vm)->alias->vm;
>   
> @@ -2599,22 +2612,16 @@ static int init_aliasing_ppgtt(struct i915_ggtt *ggtt)
>   
>   static void fini_aliasing_ppgtt(struct i915_ggtt *ggtt)
>   {
> -	struct drm_i915_private *i915 = ggtt->vm.i915;
>   	struct i915_ppgtt *ppgtt;
>   
> -	mutex_lock(&i915->drm.struct_mutex);
> -
>   	ppgtt = fetch_and_zero(&ggtt->alias);
>   	if (!ppgtt)
> -		goto out;
> +		return;
>   
>   	i915_vm_put(&ppgtt->vm);
>   
>   	ggtt->vm.vma_ops.bind_vma   = ggtt_bind_vma;
>   	ggtt->vm.vma_ops.unbind_vma = ggtt_unbind_vma;
> -
> -out:
> -	mutex_unlock(&i915->drm.struct_mutex);
>   }
>   
>   static int ggtt_reserve_guc_top(struct i915_ggtt *ggtt)
> @@ -2731,15 +2738,14 @@ int i915_init_ggtt(struct drm_i915_private *i915)
>   
>   static void ggtt_cleanup_hw(struct i915_ggtt *ggtt)
>   {
> -	struct drm_i915_private *i915 = ggtt->vm.i915;
>   	struct i915_vma *vma, *vn;
>   
> -	ggtt->vm.closed = true;
> +	atomic_set(&ggtt->vm.open, 0);
>   
>   	rcu_barrier(); /* flush the RCU'ed__i915_vm_release */
> -	flush_workqueue(i915->wq);
> +	flush_workqueue(ggtt->vm.i915->wq);
>   
> -	mutex_lock(&i915->drm.struct_mutex);
> +	mutex_lock(&ggtt->vm.mutex);
>   
>   	list_for_each_entry_safe(vma, vn, &ggtt->vm.bound_list, vm_link)
>   		WARN_ON(i915_vma_unbind(vma));
> @@ -2748,15 +2754,12 @@ static void ggtt_cleanup_hw(struct i915_ggtt *ggtt)
>   		drm_mm_remove_node(&ggtt->error_capture);
>   
>   	ggtt_release_guc_top(ggtt);
> -
> -	if (drm_mm_initialized(&ggtt->vm.mm)) {
> -		intel_vgt_deballoon(ggtt);
> -		i915_address_space_fini(&ggtt->vm);
> -	}
> +	intel_vgt_deballoon(ggtt);
>   
>   	ggtt->vm.cleanup(&ggtt->vm);
>   
> -	mutex_unlock(&i915->drm.struct_mutex);
> +	mutex_unlock(&ggtt->vm.mutex);
> +	i915_address_space_fini(&ggtt->vm);
>   
>   	arch_phys_wc_del(ggtt->mtrr);
>   	io_mapping_fini(&ggtt->iomap);
> @@ -3185,9 +3188,6 @@ int i915_ggtt_probe_hw(struct drm_i915_private *i915)
>   static int ggtt_init_hw(struct i915_ggtt *ggtt)
>   {
>   	struct drm_i915_private *i915 = ggtt->vm.i915;
> -	int ret = 0;
> -
> -	mutex_lock(&i915->drm.struct_mutex);
>   
>   	i915_address_space_init(&ggtt->vm, VM_CLASS_GGTT);
>   
> @@ -3203,18 +3203,14 @@ static int ggtt_init_hw(struct i915_ggtt *ggtt)
>   				ggtt->gmadr.start,
>   				ggtt->mappable_end)) {
>   		ggtt->vm.cleanup(&ggtt->vm);
> -		ret = -EIO;
> -		goto out;
> +		return -EIO;
>   	}
>   
>   	ggtt->mtrr = arch_phys_wc_add(ggtt->gmadr.start, ggtt->mappable_end);
>   
>   	i915_ggtt_init_fences(ggtt);
>   
> -out:
> -	mutex_unlock(&i915->drm.struct_mutex);
> -
> -	return ret;
> +	return 0;
>   }
>   
>   /**
> @@ -3286,6 +3282,7 @@ static void ggtt_restore_mappings(struct i915_ggtt *ggtt)
>   {
>   	struct i915_vma *vma, *vn;
>   	bool flush = false;
> +	int open;
>   
>   	intel_gt_check_and_clear_faults(ggtt->vm.gt);
>   
> @@ -3293,7 +3290,9 @@ static void ggtt_restore_mappings(struct i915_ggtt *ggtt)
>   
>   	/* First fill our portion of the GTT with scratch pages */
>   	ggtt->vm.clear_range(&ggtt->vm, 0, ggtt->vm.total);
> -	ggtt->vm.closed = true; /* skip rewriting PTE on VMA unbind */
> +
> +	/* Skip rewriting PTE on VMA unbind. */
> +	open = atomic_xchg(&ggtt->vm.open, 0);
>   
>   	/* clflush objects bound into the GGTT and rebind them. */
>   	list_for_each_entry_safe(vma, vn, &ggtt->vm.bound_list, vm_link) {
> @@ -3302,24 +3301,20 @@ static void ggtt_restore_mappings(struct i915_ggtt *ggtt)
>   		if (!i915_vma_is_bound(vma, I915_VMA_GLOBAL_BIND))
>   			continue;
>   
> -		mutex_unlock(&ggtt->vm.mutex);
> -
>   		if (!i915_vma_unbind(vma))
> -			goto lock;
> +			continue;
>   
> +		clear_bit(I915_VMA_GLOBAL_BIND_BIT, __i915_vma_flags(vma));
>   		WARN_ON(i915_vma_bind(vma,
>   				      obj ? obj->cache_level : 0,
> -				      PIN_UPDATE));
> +				      PIN_GLOBAL, NULL));
>   		if (obj) { /* only used during resume => exclusive access */
>   			flush |= fetch_and_zero(&obj->write_domain);
>   			obj->read_domains |= I915_GEM_DOMAIN_GTT;
>   		}
> -
> -lock:
> -		mutex_lock(&ggtt->vm.mutex);
>   	}
>   
> -	ggtt->vm.closed = false;
> +	atomic_set(&ggtt->vm.open, open);
>   	ggtt->invalidate(ggtt);
>   
>   	mutex_unlock(&ggtt->vm.mutex);
> @@ -3711,7 +3706,8 @@ int i915_gem_gtt_insert(struct i915_address_space *vm,
>   	u64 offset;
>   	int err;
>   
> -	lockdep_assert_held(&vm->i915->drm.struct_mutex);
> +	lockdep_assert_held(&vm->mutex);
> +
>   	GEM_BUG_ON(!size);
>   	GEM_BUG_ON(!IS_ALIGNED(size, I915_GTT_PAGE_SIZE));
>   	GEM_BUG_ON(alignment && !is_power_of_2(alignment));
> diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.h b/drivers/gpu/drm/i915/i915_gem_gtt.h
> index 007bdaf4ba00..e794d1f5eee8 100644
> --- a/drivers/gpu/drm/i915/i915_gem_gtt.h
> +++ b/drivers/gpu/drm/i915/i915_gem_gtt.h
> @@ -307,7 +307,7 @@ struct i915_address_space {
>   
>   	unsigned int bind_alloc;
>   
> -	bool closed;
> +	atomic_t open;
>   
>   	struct mutex mutex; /* protects vma and our lists */
>   #define VM_CLASS_GGTT 0
> @@ -575,6 +575,35 @@ static inline void i915_vm_put(struct i915_address_space *vm)
>   	kref_put(&vm->ref, i915_vm_release);
>   }
>   
> +static inline struct i915_address_space *
> +i915_vm_open(struct i915_address_space *vm)
> +{
> +	GEM_BUG_ON(!atomic_read(&vm->open));
> +	atomic_inc(&vm->open);
> +	return i915_vm_get(vm);
> +}
> +
> +static inline bool
> +i915_vm_tryopen(struct i915_address_space *vm)
> +{
> +	if (atomic_add_unless(&vm->open, 1, 0))
> +		return i915_vm_get(vm);
> +
> +	return false;
> +}
> +
> +void __i915_vm_close(struct i915_address_space *vm);
> +
> +static inline void
> +i915_vm_close(struct i915_address_space *vm)
> +{
> +	GEM_BUG_ON(!atomic_read(&vm->open));
> +	if (atomic_dec_and_test(&vm->open))
> +		__i915_vm_close(vm);
> +
> +	i915_vm_put(vm);
> +}
> +
>   int gen6_ppgtt_pin(struct i915_ppgtt *base);
>   void gen6_ppgtt_unpin(struct i915_ppgtt *base);
>   void gen6_ppgtt_unpin_all(struct i915_ppgtt *base);
> @@ -607,10 +636,9 @@ int i915_gem_gtt_insert(struct i915_address_space *vm,
>   #define PIN_OFFSET_BIAS		BIT_ULL(6)
>   #define PIN_OFFSET_FIXED	BIT_ULL(7)
>   
> -#define PIN_MBZ			BIT_ULL(8) /* I915_VMA_PIN_OVERFLOW */
> -#define PIN_GLOBAL		BIT_ULL(9) /* I915_VMA_GLOBAL_BIND */
> -#define PIN_USER		BIT_ULL(10) /* I915_VMA_LOCAL_BIND */
> -#define PIN_UPDATE		BIT_ULL(11)
> +#define PIN_UPDATE		BIT_ULL(9)
> +#define PIN_GLOBAL		BIT_ULL(10) /* I915_VMA_GLOBAL_BIND */
> +#define PIN_USER		BIT_ULL(11) /* I915_VMA_LOCAL_BIND */
>   
>   #define PIN_OFFSET_MASK		(-I915_GTT_PAGE_SIZE)
>   
> diff --git a/drivers/gpu/drm/i915/i915_perf.c b/drivers/gpu/drm/i915/i915_perf.c
> index c1b764233761..06633b4ad260 100644
> --- a/drivers/gpu/drm/i915/i915_perf.c
> +++ b/drivers/gpu/drm/i915/i915_perf.c
> @@ -1204,15 +1204,10 @@ static int i915_oa_read(struct i915_perf_stream *stream,
>   static struct intel_context *oa_pin_context(struct i915_perf_stream *stream)
>   {
>   	struct i915_gem_engines_iter it;
> -	struct drm_i915_private *i915 = stream->dev_priv;
>   	struct i915_gem_context *ctx = stream->ctx;
>   	struct intel_context *ce;
>   	int err;
>   
> -	err = i915_mutex_lock_interruptible(&i915->drm);
> -	if (err)
> -		return ERR_PTR(err);
> -
>   	for_each_gem_engine(ce, i915_gem_context_lock_engines(ctx), it) {
>   		if (ce->engine->class != RENDER_CLASS)
>   			continue;
> @@ -1229,10 +1224,6 @@ static struct intel_context *oa_pin_context(struct i915_perf_stream *stream)
>   	}
>   	i915_gem_context_unlock_engines(ctx);
>   
> -	mutex_unlock(&i915->drm.struct_mutex);
> -	if (err)
> -		return ERR_PTR(err);
> -
>   	return stream->pinned_ctx;
>   }
>   
> @@ -1331,32 +1322,22 @@ static int oa_get_render_ctx_id(struct i915_perf_stream *stream)
>    */
>   static void oa_put_render_ctx_id(struct i915_perf_stream *stream)
>   {
> -	struct drm_i915_private *dev_priv = stream->dev_priv;
>   	struct intel_context *ce;
>   
>   	stream->specific_ctx_id = INVALID_CTX_ID;
>   	stream->specific_ctx_id_mask = 0;
>   
>   	ce = fetch_and_zero(&stream->pinned_ctx);
> -	if (ce) {
> -		mutex_lock(&dev_priv->drm.struct_mutex);
> +	if (ce)
>   		intel_context_unpin(ce);
> -		mutex_unlock(&dev_priv->drm.struct_mutex);
> -	}
>   }
>   
>   static void
>   free_oa_buffer(struct i915_perf_stream *stream)
>   {
> -	struct drm_i915_private *i915 = stream->dev_priv;
> -
> -	mutex_lock(&i915->drm.struct_mutex);
> -
>   	i915_vma_unpin_and_release(&stream->oa_buffer.vma,
>   				   I915_VMA_RELEASE_MAP);
>   
> -	mutex_unlock(&i915->drm.struct_mutex);
> -
>   	stream->oa_buffer.vaddr = NULL;
>   }
>   
> @@ -1511,18 +1492,13 @@ static int alloc_oa_buffer(struct i915_perf_stream *stream)
>   	if (WARN_ON(stream->oa_buffer.vma))
>   		return -ENODEV;
>   
> -	ret = i915_mutex_lock_interruptible(&dev_priv->drm);
> -	if (ret)
> -		return ret;
> -
>   	BUILD_BUG_ON_NOT_POWER_OF_2(OA_BUFFER_SIZE);
>   	BUILD_BUG_ON(OA_BUFFER_SIZE < SZ_128K || OA_BUFFER_SIZE > SZ_16M);
>   
>   	bo = i915_gem_object_create_shmem(dev_priv, OA_BUFFER_SIZE);
>   	if (IS_ERR(bo)) {
>   		DRM_ERROR("Failed to allocate OA buffer\n");
> -		ret = PTR_ERR(bo);
> -		goto unlock;
> +		return PTR_ERR(bo);
>   	}
>   
>   	i915_gem_object_set_cache_coherency(bo, I915_CACHE_LLC);
> @@ -1546,7 +1522,7 @@ static int alloc_oa_buffer(struct i915_perf_stream *stream)
>   			 i915_ggtt_offset(stream->oa_buffer.vma),
>   			 stream->oa_buffer.vaddr);
>   
> -	goto unlock;
> +	return 0;
>   
>   err_unpin:
>   	__i915_vma_unpin(vma);
> @@ -1557,8 +1533,6 @@ static int alloc_oa_buffer(struct i915_perf_stream *stream)
>   	stream->oa_buffer.vaddr = NULL;
>   	stream->oa_buffer.vma = NULL;
>   
> -unlock:
> -	mutex_unlock(&dev_priv->drm.struct_mutex);
>   	return ret;
>   }
>   
> diff --git a/drivers/gpu/drm/i915/i915_vma.c b/drivers/gpu/drm/i915/i915_vma.c
> index 9adb85ba6daa..288679d8a6a9 100644
> --- a/drivers/gpu/drm/i915/i915_vma.c
> +++ b/drivers/gpu/drm/i915/i915_vma.c
> @@ -32,6 +32,7 @@
>   
>   #include "i915_drv.h"
>   #include "i915_globals.h"
> +#include "i915_sw_fence_work.h"
>   #include "i915_trace.h"
>   #include "i915_vma.h"
>   
> @@ -110,7 +111,8 @@ vma_create(struct drm_i915_gem_object *obj,
>   	if (vma == NULL)
>   		return ERR_PTR(-ENOMEM);
>   
> -	vma->vm = vm;
> +	mutex_init(&vma->pages_mutex);
> +	vma->vm = i915_vm_get(vm);
>   	vma->ops = &vm->vma_ops;
>   	vma->obj = obj;
>   	vma->resv = obj->base.resv;
> @@ -261,8 +263,6 @@ vma_lookup(struct drm_i915_gem_object *obj,
>    * Once created, the VMA is kept until either the object is freed, or the
>    * address space is closed.
>    *
> - * Must be called with struct_mutex held.
> - *
>    * Returns the vma, or an error pointer.
>    */
>   struct i915_vma *
> @@ -273,7 +273,7 @@ i915_vma_instance(struct drm_i915_gem_object *obj,
>   	struct i915_vma *vma;
>   
>   	GEM_BUG_ON(view && !i915_is_ggtt(vm));
> -	GEM_BUG_ON(vm->closed);
> +	GEM_BUG_ON(!atomic_read(&vm->open));
>   
>   	spin_lock(&obj->vma.lock);
>   	vma = vma_lookup(obj, vm, view);
> @@ -287,18 +287,63 @@ i915_vma_instance(struct drm_i915_gem_object *obj,
>   	return vma;
>   }
>   
> +struct i915_vma_work {
> +	struct dma_fence_work base;
> +	struct i915_vma *vma;
> +	enum i915_cache_level cache_level;
> +	unsigned int flags;
> +};
> +
> +static int __vma_bind(struct dma_fence_work *work)
> +{
> +	struct i915_vma_work *vw = container_of(work, typeof(*vw), base);
> +	struct i915_vma *vma = vw->vma;
> +	int err;
> +
> +	err = vma->ops->bind_vma(vma, vw->cache_level, vw->flags);
> +	if (err)
> +		atomic_or(I915_VMA_ERROR, &vma->flags);
> +
> +	if (vma->obj)
> +		__i915_gem_object_unpin_pages(vma->obj);
> +
> +	return err;
> +}
> +
> +static const struct dma_fence_work_ops bind_ops = {
> +	.name = "bind",
> +	.work = __vma_bind,
> +};
> +
> +struct i915_vma_work *i915_vma_work(void)
> +{
> +	struct i915_vma_work *vw;
> +
> +	vw = kzalloc(sizeof(*vw), GFP_KERNEL);

This could be reasonably high traffic to warrant a dedicated slab.

> +	if (!vw)
> +		return NULL;
> +
> +	dma_fence_work_init(&vw->base, &bind_ops);

Hm, new (for me) API.. what is the advantage/need compared to plain workers?

> +	vw->base.dma.error = -EAGAIN; /* disable the worker by default */
> +
> +	return vw;
> +}
> +
>   /**
>    * i915_vma_bind - Sets up PTEs for an VMA in it's corresponding address space.
>    * @vma: VMA to map
>    * @cache_level: mapping cache level
>    * @flags: flags like global or local mapping
> + * @work: preallocated worker for allocating and binding the PTE
>    *
>    * DMA addresses are taken from the scatter-gather table of this object (or of
>    * this VMA in case of non-default GGTT views) and PTE entries set up.
>    * Note that DMA addresses are also the only part of the SG table we care about.
>    */
> -int i915_vma_bind(struct i915_vma *vma, enum i915_cache_level cache_level,
> -		  u32 flags)
> +int i915_vma_bind(struct i915_vma *vma,
> +		  enum i915_cache_level cache_level,
> +		  u32 flags,
> +		  struct i915_vma_work *work)
>   {
>   	u32 bind_flags;
>   	u32 vma_flags;
> @@ -315,11 +360,8 @@ int i915_vma_bind(struct i915_vma *vma, enum i915_cache_level cache_level,
>   	if (GEM_DEBUG_WARN_ON(!flags))
>   		return -EINVAL;
>   
> -	bind_flags = 0;
> -	if (flags & PIN_GLOBAL)
> -		bind_flags |= I915_VMA_GLOBAL_BIND;
> -	if (flags & PIN_USER)
> -		bind_flags |= I915_VMA_LOCAL_BIND;
> +	bind_flags = flags;
> +	bind_flags &= I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND;
>   
>   	vma_flags = atomic_read(&vma->flags);
>   	vma_flags &= I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND;
> @@ -333,9 +375,32 @@ int i915_vma_bind(struct i915_vma *vma, enum i915_cache_level cache_level,
>   	GEM_BUG_ON(!vma->pages);
>   
>   	trace_i915_vma_bind(vma, bind_flags);
> -	ret = vma->ops->bind_vma(vma, cache_level, bind_flags);
> -	if (ret)
> -		return ret;
> +	if (work && (bind_flags & ~vma_flags) & vma->vm->bind_alloc) {

What is the bitwise logic checking for?

> +		work->vma = vma;
> +		work->cache_level = cache_level;
> +		work->flags = bind_flags | I915_VMA_ALLOC;
> +
> +		/*
> +		 * Note we only want to chain up to the migration fence on
> +		 * the pages (not the object itself). As we don't track that,
> +		 * yet, we have to use the exclusive fence instead.
> +		 *
> +		 * Also note that we do not want to track the async vma as
> +		 * part of the obj->resv->excl_fence as it only affects
> +		 * execution and not content or object's backing store lifetime.
> +		 */
> +		GEM_BUG_ON(i915_active_has_exclusive(&vma->active));
> +		i915_active_set_exclusive(&vma->active, &work->base.dma);

Oh right, dma_fence_work since it's not a worker but callback on 
signalling the fence... From what context it gets called (holding any 
locks?) and which locks the callback can/will take?

> +		work->base.dma.error = 0; /* enable the queue_work() */
> +
> +		if (vma->obj)
> +			__i915_gem_object_pin_pages(vma->obj);
> +	} else {
> +		GEM_BUG_ON((bind_flags & ~vma_flags) & vma->vm->bind_alloc);
> +		ret = vma->ops->bind_vma(vma, cache_level, bind_flags);
> +		if (ret)
> +			return ret;
> +	}
>   
>   	atomic_or(bind_flags, &vma->flags);
>   	return 0;
> @@ -348,9 +413,7 @@ void __iomem *i915_vma_pin_iomap(struct i915_vma *vma)
>   
>   	/* Access through the GTT requires the device to be awake. */
>   	assert_rpm_wakelock_held(&vma->vm->i915->runtime_pm);
> -
> -	lockdep_assert_held(&vma->vm->i915->drm.struct_mutex);
> -	if (WARN_ON(!i915_vma_is_map_and_fenceable(vma))) {
> +	if (GEM_WARN_ON(!i915_vma_is_map_and_fenceable(vma))) {
>   		err = -ENODEV;
>   		goto err;
>   	}
> @@ -368,7 +431,8 @@ void __iomem *i915_vma_pin_iomap(struct i915_vma *vma)
>   			goto err;
>   		}
>   
> -		vma->iomap = ptr;
> +		if (unlikely(cmpxchg(&vma->iomap, NULL, ptr)))
> +			io_mapping_unmap(ptr);

Why this change?

>   	}
>   
>   	__i915_vma_pin(vma);
> @@ -388,18 +452,12 @@ void __iomem *i915_vma_pin_iomap(struct i915_vma *vma)
>   
>   void i915_vma_flush_writes(struct i915_vma *vma)
>   {
> -	if (!i915_vma_has_ggtt_write(vma))
> -		return;
> -
> -	intel_gt_flush_ggtt_writes(vma->vm->gt);
> -
> -	i915_vma_unset_ggtt_write(vma);
> +	if (i915_vma_unset_ggtt_write(vma))
> +		intel_gt_flush_ggtt_writes(vma->vm->gt);
>   }
>   
>   void i915_vma_unpin_iomap(struct i915_vma *vma)
>   {
> -	lockdep_assert_held(&vma->vm->i915->drm.struct_mutex);
> -
>   	GEM_BUG_ON(vma->iomap == NULL);
>   
>   	i915_vma_flush_writes(vma);
> @@ -435,6 +493,9 @@ bool i915_vma_misplaced(const struct i915_vma *vma,
>   	if (!drm_mm_node_allocated(&vma->node))
>   		return false;
>   
> +	if (test_bit(I915_VMA_ERROR_BIT, __i915_vma_flags(vma)))
> +		return true;
> +
>   	if (vma->node.size < size)
>   		return true;
>   
> @@ -538,7 +599,6 @@ static void assert_bind_count(const struct drm_i915_gem_object *obj)
>   static int
>   i915_vma_insert(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
>   {
> -	struct drm_i915_private *dev_priv = vma->vm->i915;
>   	unsigned int cache_level;
>   	u64 start, end;
>   	int ret;
> @@ -564,7 +624,7 @@ i915_vma_insert(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
>   
>   	end = vma->vm->total;
>   	if (flags & PIN_MAPPABLE)
> -		end = min_t(u64, end, dev_priv->ggtt.mappable_end);
> +		end = min_t(u64, end, i915_vm_to_ggtt(vma->vm)->mappable_end);
>   	if (flags & PIN_ZONE_4G)
>   		end = min_t(u64, end, (1ULL << 32) - I915_GTT_PAGE_SIZE);
>   	GEM_BUG_ON(!IS_ALIGNED(end, I915_GTT_PAGE_SIZE));
> @@ -580,35 +640,21 @@ i915_vma_insert(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
>   		return -ENOSPC;
>   	}
>   
> -	if (vma->obj) {
> -		ret = i915_gem_object_pin_pages(vma->obj);
> -		if (ret)
> -			return ret;
> -
> +	cache_level = 0;
> +	if (vma->obj)
>   		cache_level = vma->obj->cache_level;
> -	} else {
> -		cache_level = 0;
> -	}
> -
> -	GEM_BUG_ON(vma->pages);
> -
> -	ret = vma->ops->set_pages(vma);
> -	if (ret)
> -		goto err_unpin;
>   
>   	if (flags & PIN_OFFSET_FIXED) {
>   		u64 offset = flags & PIN_OFFSET_MASK;
>   		if (!IS_ALIGNED(offset, alignment) ||
> -		    range_overflows(offset, size, end)) {
> -			ret = -EINVAL;
> -			goto err_clear;
> -		}
> +		    range_overflows(offset, size, end))

A lot of checks/ops removed from here.. I think I'll need to apply to 
figure out how flows work. Or review on high level and have faith in CI.

> +			return -EINVAL;
>   
>   		ret = i915_gem_gtt_reserve(vma->vm, &vma->node,
>   					   size, offset, cache_level,
>   					   flags);
>   		if (ret)
> -			goto err_clear;
> +			return ret;
>   	} else {
>   		/*
>   		 * We only support huge gtt pages through the 48b PPGTT,
> @@ -647,7 +693,7 @@ i915_vma_insert(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
>   					  size, alignment, cache_level,
>   					  start, end, flags);
>   		if (ret)
> -			goto err_clear;
> +			return ret;
>   
>   		GEM_BUG_ON(vma->node.start < start);
>   		GEM_BUG_ON(vma->node.start + vma->node.size > end);
> @@ -655,23 +701,15 @@ 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, cache_level));
>   
> -	mutex_lock(&vma->vm->mutex);
>   	list_add_tail(&vma->vm_link, &vma->vm->bound_list);
> -	mutex_unlock(&vma->vm->mutex);
>   
>   	if (vma->obj) {
> +		atomic_inc(&vma->obj->mm.pages_pin_count);
>   		atomic_inc(&vma->obj->bind_count);
>   		assert_bind_count(vma->obj);
>   	}
>   
>   	return 0;
> -
> -err_clear:
> -	vma->ops->clear_pages(vma);
> -err_unpin:
> -	if (vma->obj)
> -		i915_gem_object_unpin_pages(vma->obj);
> -	return ret;
>   }
>   
>   static void
> @@ -680,12 +718,7 @@ i915_vma_remove(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));
>   
> -	vma->ops->clear_pages(vma);
> -
> -	mutex_lock(&vma->vm->mutex);
> -	drm_mm_remove_node(&vma->node);
>   	list_del(&vma->vm_link);
> -	mutex_unlock(&vma->vm->mutex);
>   
>   	/*
>   	 * Since the unbound list is global, only move to that list if
> @@ -704,51 +737,206 @@ i915_vma_remove(struct i915_vma *vma)
>   		i915_gem_object_unpin_pages(obj);
>   		assert_bind_count(obj);
>   	}
> +
> +	drm_mm_remove_node(&vma->node);
>   }
>   
> -int __i915_vma_do_pin(struct i915_vma *vma,
> -		      u64 size, u64 alignment, u64 flags)
> +static bool try_fast_pin(struct i915_vma *vma, unsigned int flags)
>   {
> -	const unsigned int bound = atomic_read(&vma->flags);
> -	int ret;
> +	unsigned int bound;
> +	bool pinned = true;
>   
> -	lockdep_assert_held(&vma->vm->i915->drm.struct_mutex);
> -	GEM_BUG_ON((flags & (PIN_GLOBAL | PIN_USER)) == 0);
> -	GEM_BUG_ON((flags & PIN_GLOBAL) && !i915_vma_is_ggtt(vma));
> +	bound = atomic_read(&vma->flags);
> +	do {
> +		if (unlikely(flags & ~bound))
> +			return false;

What is this checking for?

>   
> -	if (WARN_ON(bound & I915_VMA_PIN_OVERFLOW)) {
> -		ret = -EBUSY;
> -		goto err_unpin;
> +		if (unlikely(bound & (I915_VMA_OVERFLOW | I915_VMA_ERROR)))
> +			return false;

What will the caller try to do in these cases?

> +
> +		if (!(bound & I915_VMA_PIN_MASK))
> +			goto slow;

Since fast path can go to slow, not sure what is fast referring to.

> +
> +		GEM_BUG_ON(((bound + 1) & I915_VMA_PIN_MASK) == 0);
> +	} while (!atomic_try_cmpxchg(&vma->flags, &bound, bound + 1));
> +
> +	return true;
> +
> +slow:
> +	/*
> +	 * If pin_count==0, but we are bound, check under the lock to avoid
> +	 * racing with a concurrent i915_vma_unbind().
> +	 */
> +	mutex_lock(&vma->vm->mutex);

This is never from process context to need to be interruptible?

> +	do {
> +		if (unlikely(bound & (I915_VMA_OVERFLOW | I915_VMA_ERROR))) {
> +			pinned = false;
> +			break;
> +		}
> +
> +		if (unlikely(flags & ~bound)) {
> +			pinned = false;
> +			break;
> +		}
> +	} while (!atomic_try_cmpxchg(&vma->flags, &bound, bound + 1));
> +	mutex_unlock(&vma->vm->mutex);

slow = 0 -> 1 pin transition, the rest are fast?

> +
> +	return pinned;
> +}
> +
> +static int vma_get_pages(struct i915_vma *vma)
> +{
> +	int err = 0;
> +
> +	if (atomic_add_unless(&vma->pages_count, 1, 0))
> +		return 0;
> +
> +	if (mutex_lock_interruptible(&vma->pages_mutex))
> +		return -EINTR;
> +
> +	if (!atomic_read(&vma->pages_count)) {
> +		if (vma->obj) {
> +			err = i915_gem_object_pin_pages(vma->obj);
> +			if (err)
> +				goto unlock;
> +		}
> +
> +		err = vma->ops->set_pages(vma);
> +		if (err)
> +			goto unlock;
>   	}
> +	atomic_inc(&vma->pages_count);
>   
> -	if ((bound & I915_VMA_BIND_MASK) == 0) {
> -		ret = i915_vma_insert(vma, size, alignment, flags);
> -		if (ret)
> -			goto err_unpin;
> +unlock:
> +	mutex_unlock(&vma->pages_mutex);
> +
> +	return err;
> +}
> +
> +static void __vma_put_pages(struct i915_vma *vma, unsigned int count)
> +{
> +	mutex_lock_nested(&vma->pages_mutex, SINGLE_DEPTH_NESTING);

Nesting annotation only needed on the put path? Comment to describe why 
it is needed would be good.

> +	GEM_BUG_ON(atomic_read(&vma->pages_count) < count);
> +	if (atomic_sub_return(count, &vma->pages_count) == 0) {
> +		vma->ops->clear_pages(vma);
> +		GEM_BUG_ON(vma->pages);
> +		if (vma->obj)
> +			i915_gem_object_unpin_pages(vma->obj);
>   	}
> -	GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
> +	mutex_unlock(&vma->pages_mutex);
> +}
>   
> -	ret = i915_vma_bind(vma, vma->obj ? vma->obj->cache_level : 0, flags);
> -	if (ret)
> -		goto err_remove;
> +static void vma_put_pages(struct i915_vma *vma)
> +{
> +	if (atomic_add_unless(&vma->pages_count, -1, 1))
> +		return;
>   
> -	GEM_BUG_ON(!i915_vma_is_bound(vma, I915_VMA_BIND_MASK));
> +	__vma_put_pages(vma, 1);
> +}
> +
> +static void vma_unbind_pages(struct i915_vma *vma)
> +{
> +	unsigned int count;
> +
> +	lockdep_assert_held(&vma->vm->mutex);
> +
> +	count = atomic_read(&vma->pages_count);
> +	count >>= I915_VMA_PAGES_BIAS;
> +	GEM_BUG_ON(!count);
> +
> +	__vma_put_pages(vma, count | count << I915_VMA_PAGES_BIAS);

I think we need documentation on various flags and possible states.

> +}
> +
> +int i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
> +{
> +	struct i915_vma_work *work = NULL;
> +	unsigned int bound;
> +	int err;
> +
> +	BUILD_BUG_ON(PIN_GLOBAL != I915_VMA_GLOBAL_BIND);
> +	BUILD_BUG_ON(PIN_USER != I915_VMA_LOCAL_BIND);
> +
> +	GEM_BUG_ON(flags & PIN_UPDATE);
> +	GEM_BUG_ON(!(flags & (PIN_USER | PIN_GLOBAL)));
>   
> -	if ((bound ^ atomic_read(&vma->flags)) & I915_VMA_GLOBAL_BIND)
> -		__i915_vma_set_map_and_fenceable(vma);
> +	if (try_fast_pin(vma, flags & I915_VMA_BIND_MASK))
> +		return 0;
> +
> +	err = vma_get_pages(vma);
> +	if (err)
> +		return err;
> +
> +	if (flags & PIN_USER) {
> +		work = i915_vma_work();
> +		if (!work) {
> +			err = -ENOMEM;
> +			goto err_pages;
> +		}
> +	}

Good place for a comment to explain why PIN_USER needs to go via worker 
and the rest dont.

> +
> +	err = mutex_lock_interruptible(&vma->vm->mutex);
> +	if (err)
> +		goto err_fence;
> +
> +	bound = atomic_read(&vma->flags);
> +	if (unlikely(bound & I915_VMA_ERROR)) {
> +		err = -ENOMEM;
> +		goto err_unlock;
> +	}
>   
> +	if (unlikely(!((bound + 1) & I915_VMA_PIN_MASK))) {
> +		err = -EAGAIN; /* pins are meant to be fairly temporary */
> +		goto err_unlock;
> +	}

Did not get what this is?

> +
> +	if (unlikely(!(flags & ~bound & I915_VMA_BIND_MASK))) {
> +		__i915_vma_pin(vma);
> +		goto err_unlock;
> +	}

Or this. (Comment please.)

> +
> +	err = i915_active_acquire(&vma->active);

It's dropped somewhere to release it into normal flows?

> +	if (err)
> +		goto err_unlock;
> +
> +	if (!(bound & I915_VMA_BIND_MASK)) {
> +		err = i915_vma_insert(vma, size, alignment, flags);
> +		if (err)
> +			goto err_active;
> +
> +		if (i915_is_ggtt(vma->vm))
> +			__i915_vma_set_map_and_fenceable(vma);
> +	}
> +
> +	GEM_BUG_ON(!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) */

s/bindings/pins/ ? I thought one vma is one binding by definition.. so I 
am confused.

> +	GEM_BUG_ON(bound + I915_VMA_PAGES_ACTIVE < bound);
> +	atomic_add(I915_VMA_PAGES_ACTIVE, &vma->pages_count);
> +	list_move_tail(&vma->vm_link, &vma->vm->bound_list);
> +
> +	__i915_vma_pin(vma);
> +	GEM_BUG_ON(!i915_vma_is_pinned(vma));
> +	GEM_BUG_ON(!i915_vma_is_bound(vma, flags));
>   	GEM_BUG_ON(i915_vma_misplaced(vma, size, alignment, flags));
> -	return 0;
>   
>   err_remove:
> -	if ((bound & I915_VMA_BIND_MASK) == 0) {
> +	if (!i915_vma_is_bound(vma, I915_VMA_BIND_MASK))
>   		i915_vma_remove(vma);
> -		GEM_BUG_ON(vma->pages);
> -		GEM_BUG_ON(atomic_read(&vma->flags) & I915_VMA_BIND_MASK);
> -	}
> -err_unpin:
> -	__i915_vma_unpin(vma);
> -	return ret;
> +err_active:
> +	i915_active_release(&vma->active);
> +err_unlock:
> +	mutex_unlock(&vma->vm->mutex);
> +err_fence:
> +	if (work)
> +		dma_fence_work_commit(&work->base);
> +err_pages:
> +	vma_put_pages(vma);
> +	return err;
>   }
>   
>   void i915_vma_close(struct i915_vma *vma)
> @@ -779,9 +967,6 @@ static void __i915_vma_remove_closed(struct i915_vma *vma)
>   {
>   	struct drm_i915_private *i915 = vma->vm->i915;
>   
> -	if (!i915_vma_is_closed(vma))
> -		return;
> -
>   	spin_lock_irq(&i915->gt.closed_lock);
>   	list_del_init(&vma->closed_link);
>   	spin_unlock_irq(&i915->gt.closed_lock);
> @@ -789,40 +974,35 @@ static void __i915_vma_remove_closed(struct i915_vma *vma)
>   
>   void i915_vma_reopen(struct i915_vma *vma)
>   {
> -	__i915_vma_remove_closed(vma);
> +	if (i915_vma_is_closed(vma))
> +		__i915_vma_remove_closed(vma);
>   }
>   
> -static void __i915_vma_destroy(struct i915_vma *vma)
> +void i915_vma_destroy(struct i915_vma *vma)
>   {
> -	GEM_BUG_ON(drm_mm_node_allocated(&vma->node));
> -	GEM_BUG_ON(vma->fence);
> +	if (drm_mm_node_allocated(&vma->node)) {
> +		mutex_lock(&vma->vm->mutex);
> +		atomic_and(~I915_VMA_PIN_MASK, &vma->flags);
> +		WARN_ON(i915_vma_unbind(vma));
> +		mutex_unlock(&vma->vm->mutex);
> +		GEM_BUG_ON(drm_mm_node_allocated(&vma->node));
> +	}
> +	GEM_BUG_ON(i915_vma_is_active(vma));
>   
>   	if (vma->obj) {
>   		struct drm_i915_gem_object *obj = vma->obj;
>   
>   		spin_lock(&obj->vma.lock);
>   		list_del(&vma->obj_link);
> -		rb_erase(&vma->obj_node, &vma->obj->vma.tree);
> +		rb_erase(&vma->obj_node, &obj->vma.tree);
>   		spin_unlock(&obj->vma.lock);
>   	}
>   
> -	i915_active_fini(&vma->active);
> -
> -	i915_vma_free(vma);
> -}
> -
> -void i915_vma_destroy(struct i915_vma *vma)
> -{
> -	lockdep_assert_held(&vma->vm->i915->drm.struct_mutex);
> -
> -	GEM_BUG_ON(i915_vma_is_pinned(vma));
> -
>   	__i915_vma_remove_closed(vma);
> +	i915_vm_put(vma->vm);
>   
> -	WARN_ON(i915_vma_unbind(vma));
> -	GEM_BUG_ON(i915_vma_is_active(vma));
> -
> -	__i915_vma_destroy(vma);
> +	i915_active_fini(&vma->active);
> +	i915_vma_free(vma);
>   }
>   
>   void i915_vma_parked(struct drm_i915_private *i915)
> @@ -831,12 +1011,32 @@ void i915_vma_parked(struct drm_i915_private *i915)
>   
>   	spin_lock_irq(&i915->gt.closed_lock);
>   	list_for_each_entry_safe(vma, next, &i915->gt.closed_vma, closed_link) {
> -		list_del_init(&vma->closed_link);
> +		struct drm_i915_gem_object *obj = vma->obj;
> +		struct i915_address_space *vm = vma->vm;
> +
> +		/* XXX All to avoid keeping a reference on i915_vma itself */

Does XXX signify it is a temporary state of code, or just like a comment 
but more important in some way?

> +
> +		if (!kref_get_unless_zero(&obj->base.refcount))
> +			continue;
> +
> +		if (!i915_vm_tryopen(vm)) {
> +			i915_gem_object_put(obj);
> +			obj = NULL;
> +		}
> +
>   		spin_unlock_irq(&i915->gt.closed_lock);
>   
> -		i915_vma_destroy(vma);
> +		if (obj) {
> +			i915_vma_destroy(vma);
> +			i915_gem_object_put(obj);
> +		}
> +
> +		i915_vm_close(vm);
>   
> +		/* Restart after dropping lock */
>   		spin_lock_irq(&i915->gt.closed_lock);
> +		next = list_first_entry(&i915->gt.closed_vma,
> +					typeof(*next), closed_link);
>   	}
>   	spin_unlock_irq(&i915->gt.closed_lock);
>   }
> @@ -887,6 +1087,10 @@ int i915_vma_move_to_active(struct i915_vma *vma,
>   	assert_object_held(obj);
>   	GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
>   
> +	err = i915_request_await_active(rq, &vma->active);
> +	if (err)
> +		return err;
> +
>   	/*
>   	 * Add a reference if we're newly entering the active list.
>   	 * The order in which we add operations to the retirement queue is
> @@ -927,34 +1131,19 @@ int i915_vma_unbind(struct i915_vma *vma)
>   {
>   	int ret;
>   
> -	lockdep_assert_held(&vma->vm->i915->drm.struct_mutex);
> +	lockdep_assert_held(&vma->vm->mutex);
>   
>   	/*
>   	 * First wait upon any activity as retiring the request may
>   	 * have side-effects such as unpinning or even unbinding this vma.
> +	 *
> +	 * XXX Actually waiting under the vm->mutex is a hinderance and
> +	 * should be pipelined wherever possible. In cases where that is
> +	 * unavoidable, we should lift the wait to before the mutex.
>   	 */
> -	might_sleep();
> -	if (i915_vma_is_active(vma)) {
> -		/*
> -		 * When a closed VMA is retired, it is unbound - eek.
> -		 * In order to prevent it from being recursively closed,
> -		 * take a pin on the vma so that the second unbind is
> -		 * aborted.
> -		 *
> -		 * Even more scary is that the retire callback may free
> -		 * the object (last active vma). To prevent the explosion
> -		 * we defer the actual object free to a worker that can
> -		 * only proceed once it acquires the struct_mutex (which
> -		 * we currently hold, therefore it cannot free this object
> -		 * before we are finished).
> -		 */
> -		__i915_vma_pin(vma);
> -		ret = i915_active_wait(&vma->active);
> -		__i915_vma_unpin(vma);
> -		if (ret)
> -			return ret;
> -	}
> -	GEM_BUG_ON(i915_vma_is_active(vma));
> +	ret = i915_active_wait(&vma->active);
> +	if (ret)
> +		return ret;
>   
>   	if (i915_vma_is_pinned(vma)) {
>   		vma_print_allocator(vma, "is pinned");
> @@ -975,16 +1164,12 @@ int i915_vma_unbind(struct i915_vma *vma)
>   		GEM_BUG_ON(i915_vma_has_ggtt_write(vma));
>   
>   		/* release the fence reg _after_ flushing */
> -		mutex_lock(&vma->vm->mutex);
>   		ret = i915_vma_revoke_fence(vma);
> -		mutex_unlock(&vma->vm->mutex);
>   		if (ret)
>   			return ret;
>   
>   		/* Force a pagefault for domain tracking on next user access */
> -		mutex_lock(&vma->vm->mutex);
>   		i915_vma_revoke_mmap(vma);
> -		mutex_unlock(&vma->vm->mutex);
>   
>   		__i915_vma_iounmap(vma);
>   		clear_bit(I915_VMA_CAN_FENCE_BIT, __i915_vma_flags(vma));
> @@ -992,12 +1177,13 @@ int i915_vma_unbind(struct i915_vma *vma)
>   	GEM_BUG_ON(vma->fence);
>   	GEM_BUG_ON(i915_vma_has_userfault(vma));
>   
> -	if (likely(!vma->vm->closed)) {
> +	if (likely(atomic_read(&vma->vm->open))) {
>   		trace_i915_vma_unbind(vma);
>   		vma->ops->unbind_vma(vma);
>   	}
> -	atomic_and(~I915_VMA_BIND_MASK, &vma->flags);
> +	atomic_and(~(I915_VMA_BIND_MASK | I915_VMA_ERROR), &vma->flags);
>   
> +	vma_unbind_pages(vma);
>   	i915_vma_remove(vma);
>   
>   	return 0;
> diff --git a/drivers/gpu/drm/i915/i915_vma.h b/drivers/gpu/drm/i915/i915_vma.h
> index 6053ce1dc95c..a784dc879168 100644
> --- a/drivers/gpu/drm/i915/i915_vma.h
> +++ b/drivers/gpu/drm/i915/i915_vma.h
> @@ -96,25 +96,28 @@ struct i915_vma {
>   	 * exclusive cachelines of a single page, so a maximum of 64 possible
>   	 * users.
>   	 */
> -#define I915_VMA_PIN_MASK 0xff
> -#define I915_VMA_PIN_OVERFLOW_BIT 8
> -#define I915_VMA_PIN_OVERFLOW	((int)BIT(I915_VMA_PIN_OVERFLOW_BIT))
> +#define I915_VMA_PIN_MASK 0x3ff
> +#define I915_VMA_OVERFLOW 0x200
>   
>   	/** Flags and address space this VMA is bound to */
> -#define I915_VMA_GLOBAL_BIND_BIT 9
> -#define I915_VMA_LOCAL_BIND_BIT 10
> +#define I915_VMA_GLOBAL_BIND_BIT 10
> +#define I915_VMA_LOCAL_BIND_BIT  11
>   
>   #define I915_VMA_GLOBAL_BIND	((int)BIT(I915_VMA_GLOBAL_BIND_BIT))
>   #define I915_VMA_LOCAL_BIND	((int)BIT(I915_VMA_LOCAL_BIND_BIT))
>   
> -#define I915_VMA_BIND_MASK (I915_VMA_GLOBAL_BIND | \
> -			    I915_VMA_LOCAL_BIND | \
> -			    I915_VMA_PIN_OVERFLOW)
> +#define I915_VMA_BIND_MASK (I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND)
>   
> -#define I915_VMA_GGTT_BIT	11
> -#define I915_VMA_CAN_FENCE_BIT	12
> -#define I915_VMA_USERFAULT_BIT	13
> -#define I915_VMA_GGTT_WRITE_BIT	14
> +#define I915_VMA_ALLOC_BIT	12
> +#define I915_VMA_ALLOC		((int)BIT(I915_VMA_ALLOC_BIT))
> +
> +#define I915_VMA_ERROR_BIT	13
> +#define I915_VMA_ERROR		((int)BIT(I915_VMA_ERROR_BIT))
> +
> +#define I915_VMA_GGTT_BIT	14
> +#define I915_VMA_CAN_FENCE_BIT	15
> +#define I915_VMA_USERFAULT_BIT	16
> +#define I915_VMA_GGTT_WRITE_BIT	17
>   
>   #define I915_VMA_GGTT		((int)BIT(I915_VMA_GGTT_BIT))
>   #define I915_VMA_CAN_FENCE	((int)BIT(I915_VMA_CAN_FENCE_BIT))
> @@ -123,6 +126,11 @@ 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.
> @@ -307,8 +315,12 @@ i915_vma_compare(struct i915_vma *vma,
>   	return memcmp(&vma->ggtt_view.partial, &view->partial, view->type);
>   }
>   
> -int i915_vma_bind(struct i915_vma *vma, enum i915_cache_level cache_level,
> -		  u32 flags);
> +struct i915_vma_work *i915_vma_work(void);
> +int i915_vma_bind(struct i915_vma *vma,
> +		  enum i915_cache_level cache_level,
> +		  u32 flags,
> +		  struct i915_vma_work *work);
> +
>   bool i915_gem_valid_gtt_space(struct i915_vma *vma, unsigned long cache_level);
>   bool i915_vma_misplaced(const struct i915_vma *vma,
>   			u64 size, u64 alignment, u64 flags);
> @@ -332,26 +344,8 @@ static inline void i915_vma_unlock(struct i915_vma *vma)
>   	dma_resv_unlock(vma->resv);
>   }
>   
> -int __i915_vma_do_pin(struct i915_vma *vma,
> -		      u64 size, u64 alignment, u64 flags);
> -static inline int __must_check
> -i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
> -{
> -	BUILD_BUG_ON(PIN_MBZ != I915_VMA_PIN_OVERFLOW);
> -	BUILD_BUG_ON(PIN_GLOBAL != I915_VMA_GLOBAL_BIND);
> -	BUILD_BUG_ON(PIN_USER != I915_VMA_LOCAL_BIND);
> -
> -	/* Pin early to prevent the shrinker/eviction logic from destroying
> -	 * our vma as we insert and bind.
> -	 */
> -	if (likely(((atomic_inc_return(&vma->flags) ^ flags) & I915_VMA_BIND_MASK) == 0)) {
> -		GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
> -		GEM_BUG_ON(i915_vma_misplaced(vma, size, alignment, flags));
> -		return 0;
> -	}
> -
> -	return __i915_vma_do_pin(vma, size, alignment, flags);
> -}
> +int __must_check
> +i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags);
>   
>   static inline int i915_vma_pin_count(const struct i915_vma *vma)
>   {
> @@ -366,17 +360,17 @@ static inline bool i915_vma_is_pinned(const struct i915_vma *vma)
>   static inline void __i915_vma_pin(struct i915_vma *vma)
>   {
>   	atomic_inc(&vma->flags);
> -	GEM_BUG_ON(atomic_read(&vma->flags) & I915_VMA_PIN_OVERFLOW);
> +	GEM_BUG_ON(!i915_vma_is_pinned(vma));
>   }
>   
>   static inline void __i915_vma_unpin(struct i915_vma *vma)
>   {
> +	GEM_BUG_ON(!i915_vma_is_pinned(vma));
>   	atomic_dec(&vma->flags);
>   }
>   
>   static inline void i915_vma_unpin(struct i915_vma *vma)
>   {
> -	GEM_BUG_ON(!i915_vma_is_pinned(vma));
>   	GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
>   	__i915_vma_unpin(vma);
>   }
> @@ -396,8 +390,6 @@ static inline bool i915_vma_is_bound(const struct i915_vma *vma,
>    * the caller must call i915_vma_unpin_iomap to relinquish the pinning
>    * after the iomapping is no longer required.
>    *
> - * Callers must hold the struct_mutex.
> - *
>    * Returns a valid iomapped pointer or ERR_PTR.
>    */
>   void __iomem *i915_vma_pin_iomap(struct i915_vma *vma);
> @@ -409,8 +401,8 @@ void __iomem *i915_vma_pin_iomap(struct i915_vma *vma);
>    *
>    * Unpins the previously iomapped VMA from i915_vma_pin_iomap().
>    *
> - * Callers must hold the struct_mutex. This function is only valid to be
> - * called on a VMA previously iomapped by the caller with i915_vma_pin_iomap().
> + * This function is only valid to be called on a VMA previously
> + * iomapped by the caller with i915_vma_pin_iomap().
>    */
>   void i915_vma_unpin_iomap(struct i915_vma *vma);
>   
> @@ -438,6 +430,8 @@ static inline struct page *i915_vma_first_page(struct i915_vma *vma)
>   int __must_check i915_vma_pin_fence(struct i915_vma *vma);
>   int __must_check i915_vma_revoke_fence(struct i915_vma *vma);
>   
> +int __i915_vma_pin_fence(struct i915_vma *vma);
> +
>   static inline void __i915_vma_unpin_fence(struct i915_vma *vma)
>   {
>   	GEM_BUG_ON(atomic_read(&vma->fence->pin_count) <= 0);
> @@ -455,7 +449,6 @@ static inline void __i915_vma_unpin_fence(struct i915_vma *vma)
>   static inline void
>   i915_vma_unpin_fence(struct i915_vma *vma)
>   {
> -	/* lockdep_assert_held(&vma->vm->i915->drm.struct_mutex); */
>   	if (vma->fence)
>   		__i915_vma_unpin_fence(vma);
>   }
> diff --git a/drivers/gpu/drm/i915/selftests/i915_gem_evict.c b/drivers/gpu/drm/i915/selftests/i915_gem_evict.c
> index cb30c669b1b7..ba6064147173 100644
> --- a/drivers/gpu/drm/i915/selftests/i915_gem_evict.c
> +++ b/drivers/gpu/drm/i915/selftests/i915_gem_evict.c
> @@ -106,14 +106,11 @@ static int populate_ggtt(struct drm_i915_private *i915,
>   
>   static void unpin_ggtt(struct drm_i915_private *i915)
>   {
> -	struct i915_ggtt *ggtt = &i915->ggtt;
>   	struct i915_vma *vma;
>   
> -	mutex_lock(&ggtt->vm.mutex);
>   	list_for_each_entry(vma, &i915->ggtt.vm.bound_list, vm_link)
>   		if (vma->obj->mm.quirked)
>   			i915_vma_unpin(vma);
> -	mutex_unlock(&ggtt->vm.mutex);
>   }
>   
>   static void cleanup_objects(struct drm_i915_private *i915,
> @@ -127,11 +124,7 @@ static void cleanup_objects(struct drm_i915_private *i915,
>   		i915_gem_object_put(obj);
>   	}
>   
> -	mutex_unlock(&i915->drm.struct_mutex);
> -
>   	i915_gem_drain_freed_objects(i915);
> -
> -	mutex_lock(&i915->drm.struct_mutex);
>   }
>   
>   static int igt_evict_something(void *arg)
> @@ -148,10 +141,12 @@ static int igt_evict_something(void *arg)
>   		goto cleanup;
>   
>   	/* Everything is pinned, nothing should happen */
> +	mutex_lock(&ggtt->vm.mutex);
>   	err = i915_gem_evict_something(&ggtt->vm,
>   				       I915_GTT_PAGE_SIZE, 0, 0,
>   				       0, U64_MAX,
>   				       0);
> +	mutex_unlock(&ggtt->vm.mutex);
>   	if (err != -ENOSPC) {
>   		pr_err("i915_gem_evict_something failed on a full GGTT with err=%d\n",
>   		       err);
> @@ -161,10 +156,12 @@ static int igt_evict_something(void *arg)
>   	unpin_ggtt(i915);
>   
>   	/* Everything is unpinned, we should be able to evict something */
> +	mutex_lock(&ggtt->vm.mutex);
>   	err = i915_gem_evict_something(&ggtt->vm,
>   				       I915_GTT_PAGE_SIZE, 0, 0,
>   				       0, U64_MAX,
>   				       0);
> +	mutex_unlock(&ggtt->vm.mutex);
>   	if (err) {
>   		pr_err("i915_gem_evict_something failed on a full GGTT with err=%d\n",
>   		       err);
> @@ -230,7 +227,9 @@ static int igt_evict_for_vma(void *arg)
>   		goto cleanup;
>   
>   	/* Everything is pinned, nothing should happen */
> +	mutex_lock(&ggtt->vm.mutex);
>   	err = i915_gem_evict_for_node(&ggtt->vm, &target, 0);
> +	mutex_unlock(&ggtt->vm.mutex);
>   	if (err != -ENOSPC) {
>   		pr_err("i915_gem_evict_for_node on a full GGTT returned err=%d\n",
>   		       err);
> @@ -240,7 +239,9 @@ static int igt_evict_for_vma(void *arg)
>   	unpin_ggtt(i915);
>   
>   	/* Everything is unpinned, we should be able to evict the node */
> +	mutex_lock(&ggtt->vm.mutex);
>   	err = i915_gem_evict_for_node(&ggtt->vm, &target, 0);
> +	mutex_unlock(&ggtt->vm.mutex);
>   	if (err) {
>   		pr_err("i915_gem_evict_for_node returned err=%d\n",
>   		       err);
> @@ -317,7 +318,9 @@ static int igt_evict_for_cache_color(void *arg)
>   	i915_vma_unpin(vma);
>   
>   	/* Remove just the second vma */
> +	mutex_lock(&ggtt->vm.mutex);
>   	err = i915_gem_evict_for_node(&ggtt->vm, &target, 0);
> +	mutex_unlock(&ggtt->vm.mutex);
>   	if (err) {
>   		pr_err("[0]i915_gem_evict_for_node returned err=%d\n", err);
>   		goto cleanup;
> @@ -328,7 +331,9 @@ static int igt_evict_for_cache_color(void *arg)
>   	 */
>   	target.color = I915_CACHE_L3_LLC;
>   
> +	mutex_lock(&ggtt->vm.mutex);
>   	err = i915_gem_evict_for_node(&ggtt->vm, &target, 0);
> +	mutex_unlock(&ggtt->vm.mutex);
>   	if (!err) {
>   		pr_err("[1]i915_gem_evict_for_node returned err=%d\n", err);
>   		err = -EINVAL;
> @@ -358,7 +363,9 @@ static int igt_evict_vm(void *arg)
>   		goto cleanup;
>   
>   	/* Everything is pinned, nothing should happen */
> +	mutex_lock(&ggtt->vm.mutex);
>   	err = i915_gem_evict_vm(&ggtt->vm);
> +	mutex_unlock(&ggtt->vm.mutex);
>   	if (err) {
>   		pr_err("i915_gem_evict_vm on a full GGTT returned err=%d]\n",
>   		       err);
> @@ -367,7 +374,9 @@ static int igt_evict_vm(void *arg)
>   
>   	unpin_ggtt(i915);
>   
> +	mutex_lock(&ggtt->vm.mutex);
>   	err = i915_gem_evict_vm(&ggtt->vm);
> +	mutex_unlock(&ggtt->vm.mutex);
>   	if (err) {
>   		pr_err("i915_gem_evict_vm on a full GGTT returned err=%d]\n",
>   		       err);
> @@ -408,11 +417,11 @@ static int igt_evict_contexts(void *arg)
>   	if (!HAS_FULL_PPGTT(i915))
>   		return 0;
>   
> -	mutex_lock(&i915->drm.struct_mutex);
>   	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
>   
>   	/* Reserve a block so that we know we have enough to fit a few rq */
>   	memset(&hole, 0, sizeof(hole));
> +	mutex_lock(&i915->ggtt.vm.mutex);
>   	err = i915_gem_gtt_insert(&i915->ggtt.vm, &hole,
>   				  PRETEND_GGTT_SIZE, 0, I915_COLOR_UNEVICTABLE,
>   				  0, i915->ggtt.vm.total,
> @@ -425,7 +434,9 @@ static int igt_evict_contexts(void *arg)
>   	do {
>   		struct reserved *r;
>   
> +		mutex_unlock(&i915->ggtt.vm.mutex);
>   		r = kcalloc(1, sizeof(*r), GFP_KERNEL);
> +		mutex_lock(&i915->ggtt.vm.mutex);
>   		if (!r) {
>   			err = -ENOMEM;
>   			goto out_locked;
> @@ -445,7 +456,7 @@ static int igt_evict_contexts(void *arg)
>   		count++;
>   	} while (1);
>   	drm_mm_remove_node(&hole);
> -	mutex_unlock(&i915->drm.struct_mutex);
> +	mutex_unlock(&i915->ggtt.vm.mutex);
>   	pr_info("Filled GGTT with %lu 1MiB nodes\n", count);
>   
>   	/* Overfill the GGTT with context objects and so try to evict one. */
> @@ -508,7 +519,7 @@ static int igt_evict_contexts(void *arg)
>   			break;
>   	}
>   
> -	mutex_lock(&i915->drm.struct_mutex);
> +	mutex_lock(&i915->ggtt.vm.mutex);
>   out_locked:
>   	if (igt_flush_test(i915, I915_WAIT_LOCKED))
>   		err = -EIO;
> @@ -522,8 +533,8 @@ static int igt_evict_contexts(void *arg)
>   	}
>   	if (drm_mm_node_allocated(&hole))
>   		drm_mm_remove_node(&hole);
> +	mutex_unlock(&i915->ggtt.vm.mutex);
>   	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
> -	mutex_unlock(&i915->drm.struct_mutex);
>   
>   	return err;
>   }
> @@ -545,12 +556,9 @@ int i915_gem_evict_mock_selftests(void)
>   	if (!i915)
>   		return -ENOMEM;
>   
> -	mutex_lock(&i915->drm.struct_mutex);
>   	with_intel_runtime_pm(&i915->runtime_pm, wakeref)
>   		err = i915_subtests(tests, i915);
>   
> -	mutex_unlock(&i915->drm.struct_mutex);
> -
>   	drm_dev_put(&i915->drm);
>   	return err;
>   }
> diff --git a/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c b/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
> index a90c9be95f8c..4235fa401956 100644
> --- a/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
> +++ b/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
> @@ -35,16 +35,7 @@
>   
>   static void cleanup_freed_objects(struct drm_i915_private *i915)
>   {
> -	/*
> -	 * As we may hold onto the struct_mutex for inordinate lengths of
> -	 * time, the NMI khungtaskd detector may fire for the free objects
> -	 * worker.
> -	 */
> -	mutex_unlock(&i915->drm.struct_mutex);
> -
>   	i915_gem_drain_freed_objects(i915);
> -
> -	mutex_lock(&i915->drm.struct_mutex);
>   }
>   
>   static void fake_free_pages(struct drm_i915_gem_object *obj,
> @@ -318,6 +309,17 @@ static int lowlevel_hole(struct drm_i915_private *i915,
>   	return 0;
>   }
>   
> +static int unlocked_vma_unbind(struct i915_vma *vma)
> +{
> +	int ret;
> +
> +	mutex_lock(&vma->vm->mutex);
> +	ret = i915_vma_unbind(vma);
> +	mutex_unlock(&vma->vm->mutex);
> +
> +	return ret;
> +}

There was an earlier implementation of the same helper elsewhere so 
maybe it makes sense to consolidate.

> +
>   static void close_object_list(struct list_head *objects,
>   			      struct i915_address_space *vm)
>   {
> @@ -329,7 +331,7 @@ static void close_object_list(struct list_head *objects,
>   
>   		vma = i915_vma_instance(obj, vm, NULL);
>   		if (!IS_ERR(vma))
> -			ignored = i915_vma_unbind(vma);
> +			ignored = unlocked_vma_unbind(vma);
>   		/* Only ppgtt vma may be closed before the object is freed */
>   		if (!IS_ERR(vma) && !i915_vma_is_ggtt(vma))
>   			i915_vma_close(vma);
> @@ -444,7 +446,7 @@ static int fill_hole(struct drm_i915_private *i915,
>   						goto err;
>   					}
>   
> -					err = i915_vma_unbind(vma);
> +					err = unlocked_vma_unbind(vma);
>   					if (err) {
>   						pr_err("%s(%s) (forward) unbind of vma.node=%llx + %llx failed with err=%d\n",
>   						       __func__, p->name, vma->node.start, vma->node.size,
> @@ -517,7 +519,7 @@ static int fill_hole(struct drm_i915_private *i915,
>   						goto err;
>   					}
>   
> -					err = i915_vma_unbind(vma);
> +					err = unlocked_vma_unbind(vma);
>   					if (err) {
>   						pr_err("%s(%s) (backward) unbind of vma.node=%llx + %llx failed with err=%d\n",
>   						       __func__, p->name, vma->node.start, vma->node.size,
> @@ -604,7 +606,7 @@ static int walk_hole(struct drm_i915_private *i915,
>   				goto err_close;
>   			}
>   
> -			err = i915_vma_unbind(vma);
> +			err = unlocked_vma_unbind(vma);
>   			if (err) {
>   				pr_err("%s unbind failed at %llx + %llx  with err=%d\n",
>   				       __func__, addr, vma->size, err);
> @@ -685,13 +687,13 @@ static int pot_hole(struct drm_i915_private *i915,
>   				pr_err("%s incorrect at %llx + %llx\n",
>   				       __func__, addr, vma->size);
>   				i915_vma_unpin(vma);
> -				err = i915_vma_unbind(vma);
> +				err = unlocked_vma_unbind(vma);
>   				err = -EINVAL;
>   				goto err;
>   			}
>   
>   			i915_vma_unpin(vma);
> -			err = i915_vma_unbind(vma);
> +			err = unlocked_vma_unbind(vma);
>   			GEM_BUG_ON(err);
>   		}
>   
> @@ -789,13 +791,13 @@ static int drunk_hole(struct drm_i915_private *i915,
>   				pr_err("%s incorrect at %llx + %llx\n",
>   				       __func__, addr, BIT_ULL(size));
>   				i915_vma_unpin(vma);
> -				err = i915_vma_unbind(vma);
> +				err = unlocked_vma_unbind(vma);
>   				err = -EINVAL;
>   				goto err;
>   			}
>   
>   			i915_vma_unpin(vma);
> -			err = i915_vma_unbind(vma);
> +			err = unlocked_vma_unbind(vma);
>   			GEM_BUG_ON(err);
>   
>   			if (igt_timeout(end_time,
> @@ -867,7 +869,7 @@ static int __shrink_hole(struct drm_i915_private *i915,
>   			pr_err("%s incorrect at %llx + %llx\n",
>   			       __func__, addr, size);
>   			i915_vma_unpin(vma);
> -			err = i915_vma_unbind(vma);
> +			err = unlocked_vma_unbind(vma);
>   			err = -EINVAL;
>   			break;
>   		}
> @@ -875,6 +877,15 @@ static int __shrink_hole(struct drm_i915_private *i915,
>   		i915_vma_unpin(vma);
>   		addr += size;
>   
> +		/*
> +		 * Since we are injecting allocation faults at random intervals,
> +		 * wait for this allocation to complete before we change the
> +		 * faultinjection.
> +		 */
> +		err = i915_active_wait(&vma->active);
> +		if (err)
> +			break;
> +
>   		if (igt_timeout(end_time,
>   				"%s timed out at ofset %llx [%llx - %llx]\n",
>   				__func__, addr, hole_start, hole_end)) {
> @@ -1008,21 +1019,19 @@ static int exercise_ppgtt(struct drm_i915_private *dev_priv,
>   	if (IS_ERR(file))
>   		return PTR_ERR(file);
>   
> -	mutex_lock(&dev_priv->drm.struct_mutex);
>   	ppgtt = i915_ppgtt_create(dev_priv);
>   	if (IS_ERR(ppgtt)) {
>   		err = PTR_ERR(ppgtt);
> -		goto out_unlock;
> +		goto out_free;
>   	}
>   	GEM_BUG_ON(offset_in_page(ppgtt->vm.total));
> -	GEM_BUG_ON(ppgtt->vm.closed);
> +	GEM_BUG_ON(!atomic_read(&ppgtt->vm.open));
>   
>   	err = func(dev_priv, &ppgtt->vm, 0, ppgtt->vm.total, end_time);
>   
>   	i915_vm_put(&ppgtt->vm);
> -out_unlock:
> -	mutex_unlock(&dev_priv->drm.struct_mutex);
>   
> +out_free:
>   	mock_file_free(dev_priv, file);
>   	return err;
>   }
> @@ -1085,7 +1094,6 @@ static int exercise_ggtt(struct drm_i915_private *i915,
>   	IGT_TIMEOUT(end_time);
>   	int err = 0;
>   
> -	mutex_lock(&i915->drm.struct_mutex);
>   restart:
>   	list_sort(NULL, &ggtt->vm.mm.hole_stack, sort_holes);
>   	drm_mm_for_each_hole(node, &ggtt->vm.mm, hole_start, hole_end) {
> @@ -1106,7 +1114,6 @@ static int exercise_ggtt(struct drm_i915_private *i915,
>   		last = hole_end;
>   		goto restart;
>   	}
> -	mutex_unlock(&i915->drm.struct_mutex);
>   
>   	return err;
>   }
> @@ -1148,13 +1155,9 @@ static int igt_ggtt_page(void *arg)
>   	unsigned int *order, n;
>   	int err;
>   
> -	mutex_lock(&i915->drm.struct_mutex);
> -
>   	obj = i915_gem_object_create_internal(i915, PAGE_SIZE);
> -	if (IS_ERR(obj)) {
> -		err = PTR_ERR(obj);
> -		goto out_unlock;
> -	}
> +	if (IS_ERR(obj))
> +		return PTR_ERR(obj);
>   
>   	err = i915_gem_object_pin_pages(obj);
>   	if (err)
> @@ -1222,8 +1225,6 @@ static int igt_ggtt_page(void *arg)
>   	i915_gem_object_unpin_pages(obj);
>   out_free:
>   	i915_gem_object_put(obj);
> -out_unlock:
> -	mutex_unlock(&i915->drm.struct_mutex);
>   	return err;
>   }
>   
> @@ -1234,6 +1235,9 @@ static void track_vma_bind(struct i915_vma *vma)
>   	atomic_inc(&obj->bind_count); /* track for eviction later */
>   	__i915_gem_object_pin_pages(obj);
>   
> +	GEM_BUG_ON(vma->pages);
> +	atomic_set(&vma->pages_count, I915_VMA_PAGES_ACTIVE);
> +	__i915_gem_object_pin_pages(obj);
>   	vma->pages = obj->mm.pages;
>   
>   	mutex_lock(&vma->vm->mutex);
> @@ -1330,11 +1334,13 @@ static int igt_gtt_reserve(void *arg)
>   			goto out;
>   		}
>   
> +		mutex_lock(&ggtt->vm.mutex);
>   		err = i915_gem_gtt_reserve(&ggtt->vm, &vma->node,
>   					   obj->base.size,
>   					   total,
>   					   obj->cache_level,
>   					   0);
> +		mutex_unlock(&ggtt->vm.mutex);
>   		if (err) {
>   			pr_err("i915_gem_gtt_reserve (pass 1) failed at %llu/%llu with err=%d\n",
>   			       total, ggtt->vm.total, err);
> @@ -1380,11 +1386,13 @@ static int igt_gtt_reserve(void *arg)
>   			goto out;
>   		}
>   
> +		mutex_lock(&ggtt->vm.mutex);
>   		err = i915_gem_gtt_reserve(&ggtt->vm, &vma->node,
>   					   obj->base.size,
>   					   total,
>   					   obj->cache_level,
>   					   0);
> +		mutex_unlock(&ggtt->vm.mutex);
>   		if (err) {
>   			pr_err("i915_gem_gtt_reserve (pass 2) failed at %llu/%llu with err=%d\n",
>   			       total, ggtt->vm.total, err);
> @@ -1414,7 +1422,7 @@ static int igt_gtt_reserve(void *arg)
>   			goto out;
>   		}
>   
> -		err = i915_vma_unbind(vma);
> +		err = unlocked_vma_unbind(vma);
>   		if (err) {
>   			pr_err("i915_vma_unbind failed with err=%d!\n", err);
>   			goto out;
> @@ -1424,11 +1432,13 @@ static int igt_gtt_reserve(void *arg)
>   				       2*I915_GTT_PAGE_SIZE,
>   				       I915_GTT_MIN_ALIGNMENT);
>   
> +		mutex_lock(&ggtt->vm.mutex);
>   		err = i915_gem_gtt_reserve(&ggtt->vm, &vma->node,
>   					   obj->base.size,
>   					   offset,
>   					   obj->cache_level,
>   					   0);
> +		mutex_unlock(&ggtt->vm.mutex);
>   		if (err) {
>   			pr_err("i915_gem_gtt_reserve (pass 3) failed at %llu/%llu with err=%d\n",
>   			       total, ggtt->vm.total, err);
> @@ -1497,11 +1507,13 @@ static int igt_gtt_insert(void *arg)
>   
>   	/* Check a couple of obviously invalid requests */
>   	for (ii = invalid_insert; ii->size; ii++) {
> +		mutex_lock(&ggtt->vm.mutex);
>   		err = i915_gem_gtt_insert(&ggtt->vm, &tmp,
>   					  ii->size, ii->alignment,
>   					  I915_COLOR_UNEVICTABLE,
>   					  ii->start, ii->end,
>   					  0);
> +		mutex_unlock(&ggtt->vm.mutex);
>   		if (err != -ENOSPC) {
>   			pr_err("Invalid i915_gem_gtt_insert(.size=%llx, .alignment=%llx, .start=%llx, .end=%llx) succeeded (err=%d)\n",
>   			       ii->size, ii->alignment, ii->start, ii->end,
> @@ -1537,10 +1549,12 @@ static int igt_gtt_insert(void *arg)
>   			goto out;
>   		}
>   
> +		mutex_lock(&ggtt->vm.mutex);
>   		err = i915_gem_gtt_insert(&ggtt->vm, &vma->node,
>   					  obj->base.size, 0, obj->cache_level,
>   					  0, ggtt->vm.total,
>   					  0);
> +		mutex_unlock(&ggtt->vm.mutex);
>   		if (err == -ENOSPC) {
>   			/* maxed out the GGTT space */
>   			i915_gem_object_put(obj);
> @@ -1589,16 +1603,18 @@ static int igt_gtt_insert(void *arg)
>   		GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
>   		offset = vma->node.start;
>   
> -		err = i915_vma_unbind(vma);
> +		err = unlocked_vma_unbind(vma);
>   		if (err) {
>   			pr_err("i915_vma_unbind failed with err=%d!\n", err);
>   			goto out;
>   		}
>   
> +		mutex_lock(&ggtt->vm.mutex);
>   		err = i915_gem_gtt_insert(&ggtt->vm, &vma->node,
>   					  obj->base.size, 0, obj->cache_level,
>   					  0, ggtt->vm.total,
>   					  0);
> +		mutex_unlock(&ggtt->vm.mutex);
>   		if (err) {
>   			pr_err("i915_gem_gtt_insert (pass 2) failed at %llu/%llu with err=%d\n",
>   			       total, ggtt->vm.total, err);
> @@ -1642,10 +1658,12 @@ static int igt_gtt_insert(void *arg)
>   			goto out;
>   		}
>   
> +		mutex_lock(&ggtt->vm.mutex);
>   		err = i915_gem_gtt_insert(&ggtt->vm, &vma->node,
>   					  obj->base.size, 0, obj->cache_level,
>   					  0, ggtt->vm.total,
>   					  0);
> +		mutex_unlock(&ggtt->vm.mutex);
>   		if (err) {
>   			pr_err("i915_gem_gtt_insert (pass 3) failed at %llu/%llu with err=%d\n",
>   			       total, ggtt->vm.total, err);
> @@ -1689,8 +1707,9 @@ int i915_gem_gtt_mock_selftests(void)
>   	}
>   	mock_init_ggtt(i915, ggtt);
>   
> -	mutex_lock(&i915->drm.struct_mutex);
>   	err = i915_subtests(tests, ggtt);
> +
> +	mutex_lock(&i915->drm.struct_mutex);
>   	mock_device_flush(i915);
>   	mutex_unlock(&i915->drm.struct_mutex);
>   
> diff --git a/drivers/gpu/drm/i915/selftests/i915_vma.c b/drivers/gpu/drm/i915/selftests/i915_vma.c
> index 97752deecccb..e0ca12c17a7f 100644
> --- a/drivers/gpu/drm/i915/selftests/i915_vma.c
> +++ b/drivers/gpu/drm/i915/selftests/i915_vma.c
> @@ -337,7 +337,9 @@ static int igt_vma_pin1(void *arg)
>   
>   		if (!err) {
>   			i915_vma_unpin(vma);
> +			mutex_lock(&ggtt->vm.mutex);
>   			err = i915_vma_unbind(vma);
> +			mutex_unlock(&ggtt->vm.mutex);
>   			if (err) {
>   				pr_err("Failed to unbind single page from GGTT, err=%d\n", err);
>   				goto out;
> @@ -831,8 +833,9 @@ int i915_vma_mock_selftests(void)
>   	}
>   	mock_init_ggtt(i915, ggtt);
>   
> -	mutex_lock(&i915->drm.struct_mutex);
>   	err = i915_subtests(tests, ggtt);
> +
> +	mutex_lock(&i915->drm.struct_mutex);
>   	mock_device_flush(i915);
>   	mutex_unlock(&i915->drm.struct_mutex);
>   
> @@ -879,8 +882,6 @@ static int igt_vma_remapped_gtt(void *arg)
>   	if (IS_ERR(obj))
>   		return PTR_ERR(obj);
>   
> -	mutex_lock(&i915->drm.struct_mutex);
> -
>   	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
>   
>   	for (t = types; *t; t++) {
> @@ -976,7 +977,6 @@ static int igt_vma_remapped_gtt(void *arg)
>   
>   out:
>   	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
> -	mutex_unlock(&i915->drm.struct_mutex);
>   	i915_gem_object_put(obj);
>   
>   	return err;
> 

In general, even when I did not explicitly asked for a comment, places 
where things weren't clear to me should be considered to have them added 
and probably more.

Regards,

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

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

* Re: [PATCH 13/21] drm/i915: Pull i915_vma_pin under the vm->mutex
  2019-09-17 12:37   ` Tvrtko Ursulin
@ 2019-09-17 18:56     ` Chris Wilson
  2019-09-19 13:37       ` Tvrtko Ursulin
  0 siblings, 1 reply; 53+ messages in thread
From: Chris Wilson @ 2019-09-17 18:56 UTC (permalink / raw)
  To: Tvrtko Ursulin, intel-gfx

Quoting Tvrtko Ursulin (2019-09-17 13:37:55)
> On 02/09/2019 05:02, Chris Wilson wrote:
> > diff --git a/drivers/gpu/drm/i915/gem/i915_gem_mman.c b/drivers/gpu/drm/i915/gem/i915_gem_mman.c
> > index 82db2b783123..9a8c307c5aeb 100644
> > --- a/drivers/gpu/drm/i915/gem/i915_gem_mman.c
> > +++ b/drivers/gpu/drm/i915/gem/i915_gem_mman.c
> > @@ -251,16 +251,6 @@ vm_fault_t i915_gem_fault(struct vm_fault *vmf)
> >               goto err_rpm;
> >       }
> >   
> > -     ret = i915_mutex_lock_interruptible(dev);
> > -     if (ret)
> > -             goto err_reset;
> > -
> > -     /* Access to snoopable pages through the GTT is incoherent. */
> > -     if (obj->cache_level != I915_CACHE_NONE && !HAS_LLC(i915)) {
> > -             ret = -EFAULT;
> > -             goto err_unlock;
> > -     }
> > -
> >       /* Now pin it into the GTT as needed */
> >       vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0,
> >                                      PIN_MAPPABLE |
> > @@ -293,7 +283,13 @@ vm_fault_t i915_gem_fault(struct vm_fault *vmf)
> >       }
> >       if (IS_ERR(vma)) {
> >               ret = PTR_ERR(vma);
> > -             goto err_unlock;
> > +             goto err_reset;
> > +     }
> > +
> > +     /* Access to snoopable pages through the GTT is incoherent. */
> > +     if (obj->cache_level != I915_CACHE_NONE && !HAS_LLC(i915)) {
> > +             ret = -EFAULT;
> > +             goto err_unpin;
> >       }
> 
> Why have you moved this check to after pinning?

Since we've dropped the lock around this check, the intent is to use the
pin as a guarantee that the vma cannot be changed. What we actually want
here, for clarity, is vma->cache_level.

> >   
> >       ret = i915_vma_pin_fence(vma);
> > @@ -321,14 +317,12 @@ vm_fault_t i915_gem_fault(struct vm_fault *vmf)
> >               intel_wakeref_auto(&i915->ggtt.userfault_wakeref,
> >                                  msecs_to_jiffies_timeout(CONFIG_DRM_I915_USERFAULT_AUTOSUSPEND));
> >   
> > -     i915_vma_set_ggtt_write(vma);
> > -
> > +     if (write)
> > +             i915_vma_set_ggtt_write(vma);
> 
> Noise for what this patch is concerned?

Yeah, I had to stare at an incorrect __set_bit(GGTT_WRITE) for too long.

> 
> >   err_fence:
> >       i915_vma_unpin_fence(vma);
> >   err_unpin:
> >       __i915_vma_unpin(vma);
> > -err_unlock:
> > -     mutex_unlock(&dev->struct_mutex);
> >   err_reset:
> >       intel_gt_reset_unlock(ggtt->vm.gt, srcu);
> >   err_rpm:
> > diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.c b/drivers/gpu/drm/i915/gem/i915_gem_object.c
> > index 0ef60dae23a7..dbf9be9a79f4 100644
> > --- a/drivers/gpu/drm/i915/gem/i915_gem_object.c
> > +++ b/drivers/gpu/drm/i915/gem/i915_gem_object.c
> > @@ -155,21 +155,30 @@ static void __i915_gem_free_objects(struct drm_i915_private *i915,
> >   
> >       wakeref = intel_runtime_pm_get(&i915->runtime_pm);
> >       llist_for_each_entry_safe(obj, on, freed, freed) {
> > -             struct i915_vma *vma, *vn;
> > -
> >               trace_i915_gem_object_destroy(obj);
> >   
> > -             mutex_lock(&i915->drm.struct_mutex);
> > -
> > -             list_for_each_entry_safe(vma, vn, &obj->vma.list, obj_link) {
> > -                     GEM_BUG_ON(i915_vma_is_active(vma));
> > -                     atomic_and(~I915_VMA_PIN_MASK, &vma->flags);
> > -                     i915_vma_destroy(vma);
> > +             if (!list_empty(&obj->vma.list)) {
> > +                     struct i915_vma *vma;
> > +
> > +                     /*
> > +                      * Note that the vma keeps an object reference while
> > +                      * it is active, so it *should* not sleep while we
> > +                      * destroy it. Our debug code errs insits it *might*.
> > +                      * For the moment, play along.
> > +                      */
> > +                     spin_lock(&obj->vma.lock);
> > +                     while ((vma = list_first_entry_or_null(&obj->vma.list,
> > +                                                            struct i915_vma,
> > +                                                            obj_link))) 
> 
> What is the point of having a while loop inside top-level if !list_empty 
> check? Looks theoretically racy, and even if that is irrelevant, it 
> would be clearer to just do the while loop.

We can't add more vma to the object, as there are no more refs to the
object. The challenge is in dropping the links. In the end it makes no
difference, it's an unlocked compare before the spinlock (and ugly
loop).

> >       GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
> >   
> > +     GEM_BUG_ON(vma->pages);
> >       vma->pages = obj->mm.pages;
> > +     atomic_set(&vma->pages_count, I915_VMA_PAGES_ACTIVE);
> 
> What is I915_VMA_PAGES_ACTIVE used for? Pinned? Has pages?

It's a counter for the number of unique binds as opposed to callers
merely acquiring the pages prior to binding; so that we can balance
vma_unbind_pages() which is called once (i915_vma_unbind) but
i915_vma_bind() can be called for both PIN_GLOBAL and PIN_USER.

> > diff --git a/drivers/gpu/drm/i915/gem/i915_gem_tiling.c b/drivers/gpu/drm/i915/gem/i915_gem_tiling.c
> > index ca0c2f451742..b9cfae0e4435 100644
> > --- a/drivers/gpu/drm/i915/gem/i915_gem_tiling.c
> > +++ b/drivers/gpu/drm/i915/gem/i915_gem_tiling.c
> > @@ -181,22 +181,25 @@ static int
> >   i915_gem_object_fence_prepare(struct drm_i915_gem_object *obj,
> >                             int tiling_mode, unsigned int stride)
> >   {
> > +     struct i915_ggtt *ggtt = &to_i915(obj->base.dev)->ggtt;
> >       struct i915_vma *vma;
> > -     int ret;
> > +     int ret = 0;
> >   
> >       if (tiling_mode == I915_TILING_NONE)
> >               return 0;
> >   
> > +     mutex_lock(&ggtt->vm.mutex);
> >       for_each_ggtt_vma(vma, obj) {
> >               if (i915_vma_fence_prepare(vma, tiling_mode, stride))
> >                       continue;
> 
> vma_fence_prepare doesn't need to be under mutex, but it's much easier 
> for this loop, yes?

fence_prepare is just a simple predicate, so no. What we do need though
is to pull it under the object-lock so that we don't prepare for one
tiling and bind another. Hmm.

> > @@ -333,7 +344,11 @@ static int igt_check_page_sizes(struct i915_vma *vma)
> >       struct drm_i915_private *i915 = vma->vm->i915;
> >       unsigned int supported = INTEL_INFO(i915)->page_sizes;
> >       struct drm_i915_gem_object *obj = vma->obj;
> > -     int err = 0;
> > +     int err;
> > +
> > +     err = i915_active_wait(&vma->active);
> > +     if (err)
> > +             return err;
> 
> Touched upon this in the previous email, but I think we need to put some 
> clear notes in writing, in some good place, regarding when callers need 
> to explicitly do this and when not. Don't know where though.. commit 
> message is weak. Maybe kernel doc next to i915_vma_unbind?

The couple of places that care are the exception. Though I'm
contemplating using the async bind to circumvent the stop_machine()...

> > 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 da54a718c712..aa67c02ba98c 100644
> > --- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c
> > +++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c
> > @@ -747,10 +747,7 @@ emit_rpcs_query(struct drm_i915_gem_object *obj,
> >       if (err)
> >               goto skip_request;
> >   
> > -     i915_vma_unpin(batch);
> > -     i915_vma_close(batch);
> > -     i915_vma_put(batch);
> > -
> > +     i915_vma_unpin_and_release(&batch, 0);
> 
> Is this consolidation enabled by this patch or was otherwise possible? 

At this point, it is deduplication. What prompted the change were a few
callers mixed up the potential use after free of vma->obj.

> Something rings familiar about some problem with 
> i915_vma_unpin_and_release but I can't remember what.. Semantics are a 
> bit weird though.. whereas pattern before was to i915_vma_put after 
> instantiating a vma, now it is unpin_and_release.. release being a bit 
> of an odd term in this space. There was/is no symmetry with get/put 
> anyway so I guess it's fine.

It'll be back to i915_vma_put() eventually.

> > +void i915_active_set_exclusive(struct i915_active *ref, struct dma_fence *f)
> > +{
> > +     GEM_BUG_ON(i915_active_is_idle(ref));
> > +
> > +     dma_fence_get(f);
> > +
> > +     rcu_read_lock();
> > +     if (rcu_access_pointer(ref->excl)) {
> > +             struct dma_fence *old;
> > +
> > +             old = dma_fence_get_rcu_safe(&ref->excl);
> > +             if (old) {
> > +                     if (dma_fence_remove_callback(old, &ref->excl_cb))
> > +                             atomic_dec(&ref->count);
> > +                     dma_fence_put(old);
> > +             }
> 
> Put some commentary in describing the business done with ref->excl and 
> callbacks. Or this goes away later?

void i915_active_set_exclusive(struct i915_active *ref, struct dma_fence *f)
{
        GEM_BUG_ON(i915_active_is_idle(ref));

        mutex_acquire(&ref->mutex.dep_map, 0, 0, _THIS_IP_);
        if (!__i915_active_fence_set(&ref->excl, f))
                atomic_inc(&ref->count);
        mutex_release(&ref->mutex.dep_map, 0, _THIS_IP_);
}

> > +static int excl_wait(struct i915_active *ref)
> > +{
> > +     struct dma_fence *old;
> > +     int err = 0;
> > +
> > +     if (!rcu_access_pointer(ref->excl))
> > +             return 0;
> > +
> > +     rcu_read_lock();
> > +     old = dma_fence_get_rcu_safe(&ref->excl);
> > +     rcu_read_unlock();
> 
> ref->excl can go something to NULL but not NULL to something in here?

Yes, but the wait is on the snapshot of fences at that moment in time.
For things like unbind if it becomes busy again after we start, what is
the best course of action -- currently we swear and try again.

The problem is more evident later in the comment:

/* Any fence added after the wait begins will not be auto-signaled */

which means the wait may end waiting for the background worker to flush
fences.

> >   int i915_request_await_active(struct i915_request *rq, struct i915_active *ref)
> >   {
> > -     struct active_node *it, *n;
> > -     int err;
> > -
> > -     if (RB_EMPTY_ROOT(&ref->tree))
> > -             return 0;
> > +     int err = 0;
> >   
> > -     /* await allocates and so we need to avoid hitting the shrinker */
> > -     err = i915_active_acquire(ref);
> > -     if (err)
> > -             return err;
> > +     if (rcu_access_pointer(ref->excl)) {
> 
> excl_wait used if-not-return pattern, but essentialy the same question 
> about possible races. I guess there aren't any since only retirement is 
> relevant which is something-to-NULL and that's handled fine. But I am 
> not sure, possibly comments are in order.

It's the same as any other wait on a set-of-fence, the wait is on the
snapshot of fences at that moment.

If you need stricter control (such as ensuring ordering within a
particular timeline) you have to provide the ordering on that external
timeline. That's the difference between simply waiting on the
i915_active, and setting a fence within an i915_active_fence (the latter
has a strict order).

> > @@ -109,20 +121,31 @@ int i915_gem_object_unbind(struct drm_i915_gem_object *obj,
> >       LIST_HEAD(still_in_list);
> >       int ret = 0;
> >   
> > -     lockdep_assert_held(&obj->base.dev->struct_mutex);
> > -
> >       spin_lock(&obj->vma.lock);
> >       while (!ret && (vma = list_first_entry_or_null(&obj->vma.list,
> >                                                      struct i915_vma,
> >                                                      obj_link))) {
> > +             struct i915_address_space *vm = vma->vm;
> > +
> > +             ret = -EBUSY;
> > +             if (!i915_vm_tryopen(vm))
> > +                     break;
> 
> This is some race between different paths of vm closing/destruction and 
> vma cleanup? We need a comment about it somewhere.. Here I guess is a 
> good place.

4 different paths all fighting; and coordinate by preventing the other 2
destruction paths from running as they run, and spinlocks to prevent the
other.
 
> And why break and not continue, there will be a second call coming from 
> somewhere when it fails?

It failed to unbind the object, so it would end up in a busy spin
preventing the actual vm release from removing the vma.

> > @@ -1015,14 +1016,9 @@ i915_gem_object_ggtt_pin(struct drm_i915_gem_object *obj,
> >                               return ERR_PTR(-ENOSPC);
> >               }
> >   
> > -             WARN(i915_vma_is_pinned(vma),
> > -                  "bo is already pinned in ggtt with incorrect alignment:"
> > -                  " offset=%08x, req.alignment=%llx,"
> > -                  " req.map_and_fenceable=%d, vma->map_and_fenceable=%d\n",
> > -                  i915_ggtt_offset(vma), alignment,
> > -                  !!(flags & PIN_MAPPABLE),
> > -                  i915_vma_is_map_and_fenceable(vma));
> 
> Why removing the WARN?

It's fundamentally racy. Not until you lock, flush the active, do you
know whether or not the vma is actually pinned. (Whereas previously it
was locked under struct_mutex.) With multiple simultaneous pinners, the
chance that we end up with a pin we do not like is increased. (Hmm,
early on this entire function was under a single vm->mutex, that removes
some race conditions that currently cause an unlikely failure at the
end, but such races would be ping-ponging, so both strategies have their
faults.)

> > +             mutex_lock(&vma->vm->mutex);
> >               ret = i915_vma_unbind(vma);
> > +             mutex_unlock(&vma->vm->mutex);
> >               if (ret)
> >                       return ERR_PTR(ret);
> >       }

> > @@ -127,15 +126,6 @@ i915_gem_evict_something(struct i915_address_space *vm,
> >                                   min_size, alignment, cache_level,
> >                                   start, end, mode);
> >   
> > -     /*
> > -      * Retire before we search the active list. Although we have
> > -      * reasonable accuracy in our retirement lists, we may have
> > -      * a stray pin (preventing eviction) that can only be resolved by
> > -      * retiring.
> > -      */
> > -     if (!(flags & PIN_NONBLOCK))
> > -             i915_retire_requests(dev_priv);
> 
> Not needed any more because?

It's illegal, requires struct_mutex, we do not hold struct_mutex. (Until
the end of this series when i915_retire_requests is independent of
struct_mutex and we restore it.)

> > @@ -175,7 +178,8 @@ static int ppgtt_bind_vma(struct i915_vma *vma,
> >   
> >   static void ppgtt_unbind_vma(struct i915_vma *vma)
> >   {
> > -     vma->vm->clear_range(vma->vm, vma->node.start, vma->size);
> > +     if (test_and_clear_bit(I915_VMA_ALLOC_BIT, __i915_vma_flags(vma)))
> > +             vma->vm->clear_range(vma->vm, vma->node.start, vma->size);
> 
> VMA bound and allocated are now synonyms?

clear_range decrements the pt use counters that alloc incremented.

> > +struct i915_vma_work *i915_vma_work(void)
> > +{
> > +     struct i915_vma_work *vw;
> > +
> > +     vw = kzalloc(sizeof(*vw), GFP_KERNEL);
> 
> This could be reasonably high traffic to warrant a dedicated slab.

It's also not going to survive for more than 2 kernel releases. :(

> > +     if (!vw)
> > +             return NULL;
> > +
> > +     dma_fence_work_init(&vw->base, &bind_ops);
> 
> Hm, new (for me) API.. what is the advantage/need compared to plain workers?

It is a plain worker with the dma-fence coupling we've all grown to
cherish.

> > @@ -333,9 +375,32 @@ int i915_vma_bind(struct i915_vma *vma, enum i915_cache_level cache_level,
> >       GEM_BUG_ON(!vma->pages);
> >   
> >       trace_i915_vma_bind(vma, bind_flags);
> > -     ret = vma->ops->bind_vma(vma, cache_level, bind_flags);
> > -     if (ret)
> > -             return ret;
> > +     if (work && (bind_flags & ~vma_flags) & vma->vm->bind_alloc) {
> 
> What is the bitwise logic checking for?

Whether we are adding a bind that requires allocations that the caller
requires to be async.

> > +             work->vma = vma;
> > +             work->cache_level = cache_level;
> > +             work->flags = bind_flags | I915_VMA_ALLOC;
> > +
> > +             /*
> > +              * Note we only want to chain up to the migration fence on
> > +              * the pages (not the object itself). As we don't track that,
> > +              * yet, we have to use the exclusive fence instead.
> > +              *
> > +              * Also note that we do not want to track the async vma as
> > +              * part of the obj->resv->excl_fence as it only affects
> > +              * execution and not content or object's backing store lifetime.
> > +              */
> > +             GEM_BUG_ON(i915_active_has_exclusive(&vma->active));
> > +             i915_active_set_exclusive(&vma->active, &work->base.dma);
> 
> Oh right, dma_fence_work since it's not a worker but callback on 
> signalling the fence... From what context it gets called (holding any 
> locks?) and which locks the callback can/will take?

dma_fence_work has a dma_fence_cb that schedules a work_stuct which
executes our function. (And it also has a dma-fence through which we can
signal the completion/error of that function.)

> > @@ -368,7 +431,8 @@ void __iomem *i915_vma_pin_iomap(struct i915_vma *vma)
> >                       goto err;
> >               }
> >   
> > -             vma->iomap = ptr;
> > +             if (unlikely(cmpxchg(&vma->iomap, NULL, ptr)))
> > +                     io_mapping_unmap(ptr);
> 
> Why this change?

Multiple pinners race to see who win the io_mapping competition. Each
thread does their own allocation, if first they pass ownership to the
vma, if last they delete their own io_mapping and go on to use the
winner's vma->iomap. Except an idiot forgot to update the loser's ptr
here.

> > @@ -580,35 +640,21 @@ i915_vma_insert(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
> >               return -ENOSPC;
> >       }
> >   
> > -     if (vma->obj) {
> > -             ret = i915_gem_object_pin_pages(vma->obj);
> > -             if (ret)
> > -                     return ret;
> > -
> > +     cache_level = 0;
> > +     if (vma->obj)
> >               cache_level = vma->obj->cache_level;
> > -     } else {
> > -             cache_level = 0;
> > -     }
> > -
> > -     GEM_BUG_ON(vma->pages);
> > -
> > -     ret = vma->ops->set_pages(vma);
> > -     if (ret)
> > -             goto err_unpin;
> >   
> >       if (flags & PIN_OFFSET_FIXED) {
> >               u64 offset = flags & PIN_OFFSET_MASK;
> >               if (!IS_ALIGNED(offset, alignment) ||
> > -                 range_overflows(offset, size, end)) {
> > -                     ret = -EINVAL;
> > -                     goto err_clear;
> > -             }
> > +                 range_overflows(offset, size, end))
> 
> A lot of checks/ops removed from here.. I think I'll need to apply to 
> figure out how flows work. Or review on high level and have faith in CI.

Checks didn't get dropped, just the ops moved to the caller. Those that
need to be done before the mutex have to be in the caller. This function
is now only responsible for find/reserving the place in the drm_mm.

> > +static bool try_fast_pin(struct i915_vma *vma, unsigned int flags)
> >   {
> > -     const unsigned int bound = atomic_read(&vma->flags);
> > -     int ret;
> > +     unsigned int bound;
> > +     bool pinned = true;
> >   
> > -     lockdep_assert_held(&vma->vm->i915->drm.struct_mutex);
> > -     GEM_BUG_ON((flags & (PIN_GLOBAL | PIN_USER)) == 0);
> > -     GEM_BUG_ON((flags & PIN_GLOBAL) && !i915_vma_is_ggtt(vma));
> > +     bound = atomic_read(&vma->flags);
> > +     do {
> > +             if (unlikely(flags & ~bound))
> > +                     return false;
> 
> What is this checking for?

If the requested binding (flags) is already available (bound).

> > -     if (WARN_ON(bound & I915_VMA_PIN_OVERFLOW)) {
> > -             ret = -EBUSY;
> > -             goto err_unpin;
> > +             if (unlikely(bound & (I915_VMA_OVERFLOW | I915_VMA_ERROR)))
> > +                     return false;
> 
> What will the caller try to do in these cases?

Not do the early return.

> > +
> > +             if (!(bound & I915_VMA_PIN_MASK))
> > +                     goto slow;
> 
> Since fast path can go to slow, not sure what is fast referring to.
> 
> > +
> > +             GEM_BUG_ON(((bound + 1) & I915_VMA_PIN_MASK) == 0);
> > +     } while (!atomic_try_cmpxchg(&vma->flags, &bound, bound + 1));
> > +
> > +     return true;
> > +
> > +slow:
> > +     /*
> > +      * If pin_count==0, but we are bound, check under the lock to avoid
> > +      * racing with a concurrent i915_vma_unbind().
> > +      */
> > +     mutex_lock(&vma->vm->mutex);
> 
> This is never from process context to need to be interruptible?

I started with a tristate return, didn't like it much (try() means bool
to me). It can be _interruptible here, we then do an alloc before giving
up.

> > +     do {
> > +             if (unlikely(bound & (I915_VMA_OVERFLOW | I915_VMA_ERROR))) {
> > +                     pinned = false;
> > +                     break;
> > +             }
> > +
> > +             if (unlikely(flags & ~bound)) {
> > +                     pinned = false;
> > +                     break;
> > +             }
> > +     } while (!atomic_try_cmpxchg(&vma->flags, &bound, bound + 1));
> > +     mutex_unlock(&vma->vm->mutex);
> 
> slow = 0 -> 1 pin transition, the rest are fast?

1+ are fast; unbound is slowest.

> > +
> > +     return pinned;
> > +}
> > +
> > +static int vma_get_pages(struct i915_vma *vma)
> > +{
> > +     int err = 0;
> > +
> > +     if (atomic_add_unless(&vma->pages_count, 1, 0))
> > +             return 0;
> > +
> > +     if (mutex_lock_interruptible(&vma->pages_mutex))
> > +             return -EINTR;
> > +
> > +     if (!atomic_read(&vma->pages_count)) {
> > +             if (vma->obj) {
> > +                     err = i915_gem_object_pin_pages(vma->obj);
> > +                     if (err)
> > +                             goto unlock;
> > +             }
> > +
> > +             err = vma->ops->set_pages(vma);
> > +             if (err)
> > +                     goto unlock;
> >       }
> > +     atomic_inc(&vma->pages_count);
> >   
> > -     if ((bound & I915_VMA_BIND_MASK) == 0) {
> > -             ret = i915_vma_insert(vma, size, alignment, flags);
> > -             if (ret)
> > -                     goto err_unpin;
> > +unlock:
> > +     mutex_unlock(&vma->pages_mutex);
> > +
> > +     return err;
> > +}
> > +
> > +static void __vma_put_pages(struct i915_vma *vma, unsigned int count)
> > +{
> > +     mutex_lock_nested(&vma->pages_mutex, SINGLE_DEPTH_NESTING);
> 
> Nesting annotation only needed on the put path? Comment to describe why 
> it is needed would be good.

It's an allocating mutex, so we can be called from underneath another
vma->pages_mutex. Standard misery.

> > +     GEM_BUG_ON(atomic_read(&vma->pages_count) < count);
> > +     if (atomic_sub_return(count, &vma->pages_count) == 0) {
> > +             vma->ops->clear_pages(vma);
> > +             GEM_BUG_ON(vma->pages);
> > +             if (vma->obj)
> > +                     i915_gem_object_unpin_pages(vma->obj);
> >       }
> > -     GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
> > +     mutex_unlock(&vma->pages_mutex);
> > +}
> >   
> > -     ret = i915_vma_bind(vma, vma->obj ? vma->obj->cache_level : 0, flags);
> > -     if (ret)
> > -             goto err_remove;
> > +static void vma_put_pages(struct i915_vma *vma)
> > +{
> > +     if (atomic_add_unless(&vma->pages_count, -1, 1))
> > +             return;
> >   
> > -     GEM_BUG_ON(!i915_vma_is_bound(vma, I915_VMA_BIND_MASK));
> > +     __vma_put_pages(vma, 1);
> > +}
> > +
> > +static void vma_unbind_pages(struct i915_vma *vma)
> > +{
> > +     unsigned int count;
> > +
> > +     lockdep_assert_held(&vma->vm->mutex);
> > +
> > +     count = atomic_read(&vma->pages_count);
> > +     count >>= I915_VMA_PAGES_BIAS;
> > +     GEM_BUG_ON(!count);
> > +
> > +     __vma_put_pages(vma, count | count << I915_VMA_PAGES_BIAS);
> 
> I think we need documentation on various flags and possible states.

It literally is just a bias! (2 counters within one.)

> > +int i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
> > +{
> > +     struct i915_vma_work *work = NULL;
> > +     unsigned int bound;
> > +     int err;
> > +
> > +     BUILD_BUG_ON(PIN_GLOBAL != I915_VMA_GLOBAL_BIND);
> > +     BUILD_BUG_ON(PIN_USER != I915_VMA_LOCAL_BIND);
> > +
> > +     GEM_BUG_ON(flags & PIN_UPDATE);
> > +     GEM_BUG_ON(!(flags & (PIN_USER | PIN_GLOBAL)));
> >   
> > -     if ((bound ^ atomic_read(&vma->flags)) & I915_VMA_GLOBAL_BIND)
> > -             __i915_vma_set_map_and_fenceable(vma);
> > +     if (try_fast_pin(vma, flags & I915_VMA_BIND_MASK))
> > +             return 0;
> > +
> > +     err = vma_get_pages(vma);
> > +     if (err)
> > +             return err;
> > +
> > +     if (flags & PIN_USER) {
> > +             work = i915_vma_work();
> > +             if (!work) {
> > +                     err = -ENOMEM;
> > +                     goto err_pages;
> > +             }
> > +     }
> 
> Good place for a comment to explain why PIN_USER needs to go via worker 
> and the rest dont.

Probably will change if my plans for bsw/bxt come to fruition. And
certainly will change again as we will need to pass down the fence...

> > +     err = mutex_lock_interruptible(&vma->vm->mutex);
> > +     if (err)
> > +             goto err_fence;
> > +
> > +     bound = atomic_read(&vma->flags);
> > +     if (unlikely(bound & I915_VMA_ERROR)) {
> > +             err = -ENOMEM;
> > +             goto err_unlock;
> > +     }
> >   
> > +     if (unlikely(!((bound + 1) & I915_VMA_PIN_MASK))) {
> > +             err = -EAGAIN; /* pins are meant to be fairly temporary */
> > +             goto err_unlock;
> > +     }
> 
> Did not get what this is?

The overflow test.

> > +     if (unlikely(!(flags & ~bound & I915_VMA_BIND_MASK))) {
> > +             __i915_vma_pin(vma);
> > +             goto err_unlock;
> > +     }
> 
> Or this. (Comment please.)

Already bound by another thread.

> > +
> > +     err = i915_active_acquire(&vma->active);
> 
> It's dropped somewhere to release it into normal flows?

Not many lines below before we exit. The active is acquired again for
the async operation to avoid the early release (on the success path).

> > +     if (err)
> > +             goto err_unlock;
> > +
> > +     if (!(bound & I915_VMA_BIND_MASK)) {
> > +             err = i915_vma_insert(vma, size, alignment, flags);
> > +             if (err)
> > +                     goto err_active;
> > +
> > +             if (i915_is_ggtt(vma->vm))
> > +                     __i915_vma_set_map_and_fenceable(vma);
> > +     }
> > +
> > +     GEM_BUG_ON(!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) */
> 
> s/bindings/pins/ ? I thought one vma is one binding by definition.. so I 
> am confused.

Oh no, that would be far too simple. On gen6-7 & bsw, the vma is aliased.
One vma, two binding (PIN_USER -> ggtt->alias, PIN_GLOBAL -> ggtt).

> > +     GEM_BUG_ON(bound + I915_VMA_PAGES_ACTIVE < bound);
> > +     atomic_add(I915_VMA_PAGES_ACTIVE, &vma->pages_count);
> > +     list_move_tail(&vma->vm_link, &vma->vm->bound_list);
> > +
> > +     __i915_vma_pin(vma);
> > +     GEM_BUG_ON(!i915_vma_is_pinned(vma));
> > +     GEM_BUG_ON(!i915_vma_is_bound(vma, flags));
> >       GEM_BUG_ON(i915_vma_misplaced(vma, size, alignment, flags));
> > -     return 0;
> >   
> >   err_remove:
> > -     if ((bound & I915_VMA_BIND_MASK) == 0) {
> > +     if (!i915_vma_is_bound(vma, I915_VMA_BIND_MASK))
> >               i915_vma_remove(vma);
> > -             GEM_BUG_ON(vma->pages);
> > -             GEM_BUG_ON(atomic_read(&vma->flags) & I915_VMA_BIND_MASK);
> > -     }
> > -err_unpin:
> > -     __i915_vma_unpin(vma);
> > -     return ret;
> > +err_active:
> > +     i915_active_release(&vma->active);
> > +err_unlock:
> > +     mutex_unlock(&vma->vm->mutex);
> > +err_fence:
> > +     if (work)
> > +             dma_fence_work_commit(&work->base);
> > +err_pages:
> > +     vma_put_pages(vma);
> > +     return err;
> >   }

> >   void i915_vma_parked(struct drm_i915_private *i915)
> > @@ -831,12 +1011,32 @@ void i915_vma_parked(struct drm_i915_private *i915)
> >   
> >       spin_lock_irq(&i915->gt.closed_lock);
> >       list_for_each_entry_safe(vma, next, &i915->gt.closed_vma, closed_link) {
> > -             list_del_init(&vma->closed_link);
> > +             struct drm_i915_gem_object *obj = vma->obj;
> > +             struct i915_address_space *vm = vma->vm;
> > +
> > +             /* XXX All to avoid keeping a reference on i915_vma itself */
> 
> Does XXX signify it is a temporary state of code, or just like a comment 
> but more important in some way?

It's a critique of the ugly code (the comment you asked for before) that
is required because I didn't want to have to throw away the entire lmem
object handling code.

> > @@ -976,7 +977,6 @@ static int igt_vma_remapped_gtt(void *arg)
> >   
> >   out:
> >       intel_runtime_pm_put(&i915->runtime_pm, wakeref);
> > -     mutex_unlock(&i915->drm.struct_mutex);
> >       i915_gem_object_put(obj);
> >   
> >       return err;
> > 
> 
> In general, even when I did not explicitly asked for a comment, places 
> where things weren't clear to me should be considered to have them added 
> and probably more.

How else do I know what isn't clear to you... ;)
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* Re: [PATCH 13/21] drm/i915: Pull i915_vma_pin under the vm->mutex
  2019-09-17 18:56     ` Chris Wilson
@ 2019-09-19 13:37       ` Tvrtko Ursulin
  2019-09-19 14:05         ` Chris Wilson
  0 siblings, 1 reply; 53+ messages in thread
From: Tvrtko Ursulin @ 2019-09-19 13:37 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 17/09/2019 19:56, Chris Wilson wrote:
> Quoting Tvrtko Ursulin (2019-09-17 13:37:55)
>> On 02/09/2019 05:02, Chris Wilson wrote:
>>> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_mman.c b/drivers/gpu/drm/i915/gem/i915_gem_mman.c
>>> index 82db2b783123..9a8c307c5aeb 100644
>>> --- a/drivers/gpu/drm/i915/gem/i915_gem_mman.c
>>> +++ b/drivers/gpu/drm/i915/gem/i915_gem_mman.c
>>> @@ -251,16 +251,6 @@ vm_fault_t i915_gem_fault(struct vm_fault *vmf)
>>>                goto err_rpm;
>>>        }
>>>    
>>> -     ret = i915_mutex_lock_interruptible(dev);
>>> -     if (ret)
>>> -             goto err_reset;
>>> -
>>> -     /* Access to snoopable pages through the GTT is incoherent. */
>>> -     if (obj->cache_level != I915_CACHE_NONE && !HAS_LLC(i915)) {
>>> -             ret = -EFAULT;
>>> -             goto err_unlock;
>>> -     }
>>> -
>>>        /* Now pin it into the GTT as needed */
>>>        vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0,
>>>                                       PIN_MAPPABLE |
>>> @@ -293,7 +283,13 @@ vm_fault_t i915_gem_fault(struct vm_fault *vmf)
>>>        }
>>>        if (IS_ERR(vma)) {
>>>                ret = PTR_ERR(vma);
>>> -             goto err_unlock;
>>> +             goto err_reset;
>>> +     }
>>> +
>>> +     /* Access to snoopable pages through the GTT is incoherent. */
>>> +     if (obj->cache_level != I915_CACHE_NONE && !HAS_LLC(i915)) {
>>> +             ret = -EFAULT;
>>> +             goto err_unpin;
>>>        }
>>
>> Why have you moved this check to after pinning?
> 
> Since we've dropped the lock around this check, the intent is to use the
> pin as a guarantee that the vma cannot be changed. What we actually want
> here, for clarity, is vma->cache_level.
> 
>>>    
>>>        ret = i915_vma_pin_fence(vma);
>>> @@ -321,14 +317,12 @@ vm_fault_t i915_gem_fault(struct vm_fault *vmf)
>>>                intel_wakeref_auto(&i915->ggtt.userfault_wakeref,
>>>                                   msecs_to_jiffies_timeout(CONFIG_DRM_I915_USERFAULT_AUTOSUSPEND));
>>>    
>>> -     i915_vma_set_ggtt_write(vma);
>>> -
>>> +     if (write)
>>> +             i915_vma_set_ggtt_write(vma);
>>
>> Noise for what this patch is concerned?
> 
> Yeah, I had to stare at an incorrect __set_bit(GGTT_WRITE) for too long.
> 
>>
>>>    err_fence:
>>>        i915_vma_unpin_fence(vma);
>>>    err_unpin:
>>>        __i915_vma_unpin(vma);
>>> -err_unlock:
>>> -     mutex_unlock(&dev->struct_mutex);
>>>    err_reset:
>>>        intel_gt_reset_unlock(ggtt->vm.gt, srcu);
>>>    err_rpm:
>>> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.c b/drivers/gpu/drm/i915/gem/i915_gem_object.c
>>> index 0ef60dae23a7..dbf9be9a79f4 100644
>>> --- a/drivers/gpu/drm/i915/gem/i915_gem_object.c
>>> +++ b/drivers/gpu/drm/i915/gem/i915_gem_object.c
>>> @@ -155,21 +155,30 @@ static void __i915_gem_free_objects(struct drm_i915_private *i915,
>>>    
>>>        wakeref = intel_runtime_pm_get(&i915->runtime_pm);
>>>        llist_for_each_entry_safe(obj, on, freed, freed) {
>>> -             struct i915_vma *vma, *vn;
>>> -
>>>                trace_i915_gem_object_destroy(obj);
>>>    
>>> -             mutex_lock(&i915->drm.struct_mutex);
>>> -
>>> -             list_for_each_entry_safe(vma, vn, &obj->vma.list, obj_link) {
>>> -                     GEM_BUG_ON(i915_vma_is_active(vma));
>>> -                     atomic_and(~I915_VMA_PIN_MASK, &vma->flags);
>>> -                     i915_vma_destroy(vma);
>>> +             if (!list_empty(&obj->vma.list)) {
>>> +                     struct i915_vma *vma;
>>> +
>>> +                     /*
>>> +                      * Note that the vma keeps an object reference while
>>> +                      * it is active, so it *should* not sleep while we
>>> +                      * destroy it. Our debug code errs insits it *might*.
>>> +                      * For the moment, play along.
>>> +                      */
>>> +                     spin_lock(&obj->vma.lock);
>>> +                     while ((vma = list_first_entry_or_null(&obj->vma.list,
>>> +                                                            struct i915_vma,
>>> +                                                            obj_link)))
>>
>> What is the point of having a while loop inside top-level if !list_empty
>> check? Looks theoretically racy, and even if that is irrelevant, it
>> would be clearer to just do the while loop.
> 
> We can't add more vma to the object, as there are no more refs to the
> object. The challenge is in dropping the links. In the end it makes no
> difference, it's an unlocked compare before the spinlock (and ugly
> loop).

I was simply thinking it would be one indent level less for readability 
both on the visual and conceptual level. But doesn't matter hugely.

> 
>>>        GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
>>>    
>>> +     GEM_BUG_ON(vma->pages);
>>>        vma->pages = obj->mm.pages;
>>> +     atomic_set(&vma->pages_count, I915_VMA_PAGES_ACTIVE);
>>
>> What is I915_VMA_PAGES_ACTIVE used for? Pinned? Has pages?
> 
> It's a counter for the number of unique binds as opposed to callers
> merely acquiring the pages prior to binding; so that we can balance
> vma_unbind_pages() which is called once (i915_vma_unbind) but
> i915_vma_bind() can be called for both PIN_GLOBAL and PIN_USER.
> 
>>> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_tiling.c b/drivers/gpu/drm/i915/gem/i915_gem_tiling.c
>>> index ca0c2f451742..b9cfae0e4435 100644
>>> --- a/drivers/gpu/drm/i915/gem/i915_gem_tiling.c
>>> +++ b/drivers/gpu/drm/i915/gem/i915_gem_tiling.c
>>> @@ -181,22 +181,25 @@ static int
>>>    i915_gem_object_fence_prepare(struct drm_i915_gem_object *obj,
>>>                              int tiling_mode, unsigned int stride)
>>>    {
>>> +     struct i915_ggtt *ggtt = &to_i915(obj->base.dev)->ggtt;
>>>        struct i915_vma *vma;
>>> -     int ret;
>>> +     int ret = 0;
>>>    
>>>        if (tiling_mode == I915_TILING_NONE)
>>>                return 0;
>>>    
>>> +     mutex_lock(&ggtt->vm.mutex);
>>>        for_each_ggtt_vma(vma, obj) {
>>>                if (i915_vma_fence_prepare(vma, tiling_mode, stride))
>>>                        continue;
>>
>> vma_fence_prepare doesn't need to be under mutex, but it's much easier
>> for this loop, yes?
> 
> fence_prepare is just a simple predicate, so no. What we do need though
> is to pull it under the object-lock so that we don't prepare for one
> tiling and bind another. Hmm.
> 
>>> @@ -333,7 +344,11 @@ static int igt_check_page_sizes(struct i915_vma *vma)
>>>        struct drm_i915_private *i915 = vma->vm->i915;
>>>        unsigned int supported = INTEL_INFO(i915)->page_sizes;
>>>        struct drm_i915_gem_object *obj = vma->obj;
>>> -     int err = 0;
>>> +     int err;
>>> +
>>> +     err = i915_active_wait(&vma->active);
>>> +     if (err)
>>> +             return err;
>>
>> Touched upon this in the previous email, but I think we need to put some
>> clear notes in writing, in some good place, regarding when callers need
>> to explicitly do this and when not. Don't know where though.. commit
>> message is weak. Maybe kernel doc next to i915_vma_unbind?
> 
> The couple of places that care are the exception. Though I'm
> contemplating using the async bind to circumvent the stop_machine()...
> 
>>> 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 da54a718c712..aa67c02ba98c 100644
>>> --- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c
>>> +++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c
>>> @@ -747,10 +747,7 @@ emit_rpcs_query(struct drm_i915_gem_object *obj,
>>>        if (err)
>>>                goto skip_request;
>>>    
>>> -     i915_vma_unpin(batch);
>>> -     i915_vma_close(batch);
>>> -     i915_vma_put(batch);
>>> -
>>> +     i915_vma_unpin_and_release(&batch, 0);
>>
>> Is this consolidation enabled by this patch or was otherwise possible?
> 
> At this point, it is deduplication. What prompted the change were a few
> callers mixed up the potential use after free of vma->obj.
> 
>> Something rings familiar about some problem with
>> i915_vma_unpin_and_release but I can't remember what.. Semantics are a
>> bit weird though.. whereas pattern before was to i915_vma_put after
>> instantiating a vma, now it is unpin_and_release.. release being a bit
>> of an odd term in this space. There was/is no symmetry with get/put
>> anyway so I guess it's fine.
> 
> It'll be back to i915_vma_put() eventually.
> 
>>> +void i915_active_set_exclusive(struct i915_active *ref, struct dma_fence *f)
>>> +{
>>> +     GEM_BUG_ON(i915_active_is_idle(ref));
>>> +
>>> +     dma_fence_get(f);
>>> +
>>> +     rcu_read_lock();
>>> +     if (rcu_access_pointer(ref->excl)) {
>>> +             struct dma_fence *old;
>>> +
>>> +             old = dma_fence_get_rcu_safe(&ref->excl);
>>> +             if (old) {
>>> +                     if (dma_fence_remove_callback(old, &ref->excl_cb))
>>> +                             atomic_dec(&ref->count);
>>> +                     dma_fence_put(old);
>>> +             }
>>
>> Put some commentary in describing the business done with ref->excl and
>> callbacks. Or this goes away later?
> 
> void i915_active_set_exclusive(struct i915_active *ref, struct dma_fence *f)
> {
>          GEM_BUG_ON(i915_active_is_idle(ref));
> 
>          mutex_acquire(&ref->mutex.dep_map, 0, 0, _THIS_IP_);
>          if (!__i915_active_fence_set(&ref->excl, f))
>                  atomic_inc(&ref->count);
>          mutex_release(&ref->mutex.dep_map, 0, _THIS_IP_);
> }

What is this?

> 
>>> +static int excl_wait(struct i915_active *ref)
>>> +{
>>> +     struct dma_fence *old;
>>> +     int err = 0;
>>> +
>>> +     if (!rcu_access_pointer(ref->excl))
>>> +             return 0;
>>> +
>>> +     rcu_read_lock();
>>> +     old = dma_fence_get_rcu_safe(&ref->excl);
>>> +     rcu_read_unlock();
>>
>> ref->excl can go something to NULL but not NULL to something in here?
> 
> Yes, but the wait is on the snapshot of fences at that moment in time.
> For things like unbind if it becomes busy again after we start, what is
> the best course of action -- currently we swear and try again.
> 
> The problem is more evident later in the comment:
> 
> /* Any fence added after the wait begins will not be auto-signaled */
> 
> which means the wait may end waiting for the background worker to flush
> fences.
> 
>>>    int i915_request_await_active(struct i915_request *rq, struct i915_active *ref)
>>>    {
>>> -     struct active_node *it, *n;
>>> -     int err;
>>> -
>>> -     if (RB_EMPTY_ROOT(&ref->tree))
>>> -             return 0;
>>> +     int err = 0;
>>>    
>>> -     /* await allocates and so we need to avoid hitting the shrinker */
>>> -     err = i915_active_acquire(ref);
>>> -     if (err)
>>> -             return err;
>>> +     if (rcu_access_pointer(ref->excl)) {
>>
>> excl_wait used if-not-return pattern, but essentialy the same question
>> about possible races. I guess there aren't any since only retirement is
>> relevant which is something-to-NULL and that's handled fine. But I am
>> not sure, possibly comments are in order.
> 
> It's the same as any other wait on a set-of-fence, the wait is on the
> snapshot of fences at that moment.
> 
> If you need stricter control (such as ensuring ordering within a
> particular timeline) you have to provide the ordering on that external
> timeline. That's the difference between simply waiting on the
> i915_active, and setting a fence within an i915_active_fence (the latter
> has a strict order).
> 
>>> @@ -109,20 +121,31 @@ int i915_gem_object_unbind(struct drm_i915_gem_object *obj,
>>>        LIST_HEAD(still_in_list);
>>>        int ret = 0;
>>>    
>>> -     lockdep_assert_held(&obj->base.dev->struct_mutex);
>>> -
>>>        spin_lock(&obj->vma.lock);
>>>        while (!ret && (vma = list_first_entry_or_null(&obj->vma.list,
>>>                                                       struct i915_vma,
>>>                                                       obj_link))) {
>>> +             struct i915_address_space *vm = vma->vm;
>>> +
>>> +             ret = -EBUSY;
>>> +             if (!i915_vm_tryopen(vm))
>>> +                     break;
>>
>> This is some race between different paths of vm closing/destruction and
>> vma cleanup? We need a comment about it somewhere.. Here I guess is a
>> good place.
> 
> 4 different paths all fighting; and coordinate by preventing the other 2
> destruction paths from running as they run, and spinlocks to prevent the
> other.
>   
>> And why break and not continue, there will be a second call coming from
>> somewhere when it fails?
> 
> It failed to unbind the object, so it would end up in a busy spin
> preventing the actual vm release from removing the vma.

But it could proceed to unbind what can be unbound. Otherwise is it 
guaranteed to be called again when it fails, so eventually it will clean 
up everything?

> 
>>> @@ -1015,14 +1016,9 @@ i915_gem_object_ggtt_pin(struct drm_i915_gem_object *obj,
>>>                                return ERR_PTR(-ENOSPC);
>>>                }
>>>    
>>> -             WARN(i915_vma_is_pinned(vma),
>>> -                  "bo is already pinned in ggtt with incorrect alignment:"
>>> -                  " offset=%08x, req.alignment=%llx,"
>>> -                  " req.map_and_fenceable=%d, vma->map_and_fenceable=%d\n",
>>> -                  i915_ggtt_offset(vma), alignment,
>>> -                  !!(flags & PIN_MAPPABLE),
>>> -                  i915_vma_is_map_and_fenceable(vma));
>>
>> Why removing the WARN?
> 
> It's fundamentally racy. Not until you lock, flush the active, do you
> know whether or not the vma is actually pinned. (Whereas previously it
> was locked under struct_mutex.) With multiple simultaneous pinners, the
> chance that we end up with a pin we do not like is increased. (Hmm,
> early on this entire function was under a single vm->mutex, that removes
> some race conditions that currently cause an unlikely failure at the
> end, but such races would be ping-ponging, so both strategies have their
> faults.)
> 
>>> +             mutex_lock(&vma->vm->mutex);
>>>                ret = i915_vma_unbind(vma);
>>> +             mutex_unlock(&vma->vm->mutex);
>>>                if (ret)
>>>                        return ERR_PTR(ret);
>>>        }
> 
>>> @@ -127,15 +126,6 @@ i915_gem_evict_something(struct i915_address_space *vm,
>>>                                    min_size, alignment, cache_level,
>>>                                    start, end, mode);
>>>    
>>> -     /*
>>> -      * Retire before we search the active list. Although we have
>>> -      * reasonable accuracy in our retirement lists, we may have
>>> -      * a stray pin (preventing eviction) that can only be resolved by
>>> -      * retiring.
>>> -      */
>>> -     if (!(flags & PIN_NONBLOCK))
>>> -             i915_retire_requests(dev_priv);
>>
>> Not needed any more because?
> 
> It's illegal, requires struct_mutex, we do not hold struct_mutex. (Until
> the end of this series when i915_retire_requests is independent of
> struct_mutex and we restore it.)
> 
>>> @@ -175,7 +178,8 @@ static int ppgtt_bind_vma(struct i915_vma *vma,
>>>    
>>>    static void ppgtt_unbind_vma(struct i915_vma *vma)
>>>    {
>>> -     vma->vm->clear_range(vma->vm, vma->node.start, vma->size);
>>> +     if (test_and_clear_bit(I915_VMA_ALLOC_BIT, __i915_vma_flags(vma)))
>>> +             vma->vm->clear_range(vma->vm, vma->node.start, vma->size);
>>
>> VMA bound and allocated are now synonyms?
> 
> clear_range decrements the pt use counters that alloc incremented.

I think I was tasking for a description of I915_VMA_ALLOC_BIT, but not 
sure any more.

> 
>>> +struct i915_vma_work *i915_vma_work(void)
>>> +{
>>> +     struct i915_vma_work *vw;
>>> +
>>> +     vw = kzalloc(sizeof(*vw), GFP_KERNEL);
>>
>> This could be reasonably high traffic to warrant a dedicated slab.
> 
> It's also not going to survive for more than 2 kernel releases. :(
> 
>>> +     if (!vw)
>>> +             return NULL;
>>> +
>>> +     dma_fence_work_init(&vw->base, &bind_ops);
>>
>> Hm, new (for me) API.. what is the advantage/need compared to plain workers?
> 
> It is a plain worker with the dma-fence coupling we've all grown to
> cherish.
> 
>>> @@ -333,9 +375,32 @@ int i915_vma_bind(struct i915_vma *vma, enum i915_cache_level cache_level,
>>>        GEM_BUG_ON(!vma->pages);
>>>    
>>>        trace_i915_vma_bind(vma, bind_flags);
>>> -     ret = vma->ops->bind_vma(vma, cache_level, bind_flags);
>>> -     if (ret)
>>> -             return ret;
>>> +     if (work && (bind_flags & ~vma_flags) & vma->vm->bind_alloc) {
>>
>> What is the bitwise logic checking for?
> 
> Whether we are adding a bind that requires allocations that the caller
> requires to be async.
> 
>>> +             work->vma = vma;
>>> +             work->cache_level = cache_level;
>>> +             work->flags = bind_flags | I915_VMA_ALLOC;
>>> +
>>> +             /*
>>> +              * Note we only want to chain up to the migration fence on
>>> +              * the pages (not the object itself). As we don't track that,
>>> +              * yet, we have to use the exclusive fence instead.
>>> +              *
>>> +              * Also note that we do not want to track the async vma as
>>> +              * part of the obj->resv->excl_fence as it only affects
>>> +              * execution and not content or object's backing store lifetime.
>>> +              */
>>> +             GEM_BUG_ON(i915_active_has_exclusive(&vma->active));
>>> +             i915_active_set_exclusive(&vma->active, &work->base.dma);
>>
>> Oh right, dma_fence_work since it's not a worker but callback on
>> signalling the fence... From what context it gets called (holding any
>> locks?) and which locks the callback can/will take?
> 
> dma_fence_work has a dma_fence_cb that schedules a work_stuct which
> executes our function. (And it also has a dma-fence through which we can
> signal the completion/error of that function.)

And this is to avoid some allocation somewhere?

> 
>>> @@ -368,7 +431,8 @@ void __iomem *i915_vma_pin_iomap(struct i915_vma *vma)
>>>                        goto err;
>>>                }
>>>    
>>> -             vma->iomap = ptr;
>>> +             if (unlikely(cmpxchg(&vma->iomap, NULL, ptr)))
>>> +                     io_mapping_unmap(ptr);
>>
>> Why this change?
> 
> Multiple pinners race to see who win the io_mapping competition. Each
> thread does their own allocation, if first they pass ownership to the
> vma, if last they delete their own io_mapping and go on to use the
> winner's vma->iomap. Except an idiot forgot to update the loser's ptr
> here.
> 
>>> @@ -580,35 +640,21 @@ i915_vma_insert(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
>>>                return -ENOSPC;
>>>        }
>>>    
>>> -     if (vma->obj) {
>>> -             ret = i915_gem_object_pin_pages(vma->obj);
>>> -             if (ret)
>>> -                     return ret;
>>> -
>>> +     cache_level = 0;
>>> +     if (vma->obj)
>>>                cache_level = vma->obj->cache_level;
>>> -     } else {
>>> -             cache_level = 0;
>>> -     }
>>> -
>>> -     GEM_BUG_ON(vma->pages);
>>> -
>>> -     ret = vma->ops->set_pages(vma);
>>> -     if (ret)
>>> -             goto err_unpin;
>>>    
>>>        if (flags & PIN_OFFSET_FIXED) {
>>>                u64 offset = flags & PIN_OFFSET_MASK;
>>>                if (!IS_ALIGNED(offset, alignment) ||
>>> -                 range_overflows(offset, size, end)) {
>>> -                     ret = -EINVAL;
>>> -                     goto err_clear;
>>> -             }
>>> +                 range_overflows(offset, size, end))
>>
>> A lot of checks/ops removed from here.. I think I'll need to apply to
>> figure out how flows work. Or review on high level and have faith in CI.
> 
> Checks didn't get dropped, just the ops moved to the caller. Those that
> need to be done before the mutex have to be in the caller. This function
> is now only responsible for find/reserving the place in the drm_mm.
> 
>>> +static bool try_fast_pin(struct i915_vma *vma, unsigned int flags)
>>>    {
>>> -     const unsigned int bound = atomic_read(&vma->flags);
>>> -     int ret;
>>> +     unsigned int bound;
>>> +     bool pinned = true;
>>>    
>>> -     lockdep_assert_held(&vma->vm->i915->drm.struct_mutex);
>>> -     GEM_BUG_ON((flags & (PIN_GLOBAL | PIN_USER)) == 0);
>>> -     GEM_BUG_ON((flags & PIN_GLOBAL) && !i915_vma_is_ggtt(vma));
>>> +     bound = atomic_read(&vma->flags);
>>> +     do {
>>> +             if (unlikely(flags & ~bound))
>>> +                     return false;
>>
>> What is this checking for?
> 
> If the requested binding (flags) is already available (bound).
> 
>>> -     if (WARN_ON(bound & I915_VMA_PIN_OVERFLOW)) {
>>> -             ret = -EBUSY;
>>> -             goto err_unpin;
>>> +             if (unlikely(bound & (I915_VMA_OVERFLOW | I915_VMA_ERROR)))
>>> +                     return false;
>>
>> What will the caller try to do in these cases?
> 
> Not do the early return.

How it will recover from error or overflow was the question I was aiming 
at. But that looks to have been before I figured out what do you really 
mean by fast in this context. Fast/slow terminology is not the clearest 
here but I have no better suggestions at the moment.

> 
>>> +
>>> +             if (!(bound & I915_VMA_PIN_MASK))
>>> +                     goto slow;
>>
>> Since fast path can go to slow, not sure what is fast referring to.
>>
>>> +
>>> +             GEM_BUG_ON(((bound + 1) & I915_VMA_PIN_MASK) == 0);
>>> +     } while (!atomic_try_cmpxchg(&vma->flags, &bound, bound + 1));
>>> +
>>> +     return true;
>>> +
>>> +slow:
>>> +     /*
>>> +      * If pin_count==0, but we are bound, check under the lock to avoid
>>> +      * racing with a concurrent i915_vma_unbind().
>>> +      */
>>> +     mutex_lock(&vma->vm->mutex);
>>
>> This is never from process context to need to be interruptible?
> 
> I started with a tristate return, didn't like it much (try() means bool
> to me). It can be _interruptible here, we then do an alloc before giving
> up.
> 
>>> +     do {
>>> +             if (unlikely(bound & (I915_VMA_OVERFLOW | I915_VMA_ERROR))) {
>>> +                     pinned = false;
>>> +                     break;
>>> +             }
>>> +
>>> +             if (unlikely(flags & ~bound)) {
>>> +                     pinned = false;
>>> +                     break;
>>> +             }
>>> +     } while (!atomic_try_cmpxchg(&vma->flags, &bound, bound + 1));
>>> +     mutex_unlock(&vma->vm->mutex);
>>
>> slow = 0 -> 1 pin transition, the rest are fast?
> 
> 1+ are fast; unbound is slowest.
> 
>>> +
>>> +     return pinned;
>>> +}
>>> +
>>> +static int vma_get_pages(struct i915_vma *vma)
>>> +{
>>> +     int err = 0;
>>> +
>>> +     if (atomic_add_unless(&vma->pages_count, 1, 0))
>>> +             return 0;
>>> +
>>> +     if (mutex_lock_interruptible(&vma->pages_mutex))
>>> +             return -EINTR;
>>> +
>>> +     if (!atomic_read(&vma->pages_count)) {
>>> +             if (vma->obj) {
>>> +                     err = i915_gem_object_pin_pages(vma->obj);
>>> +                     if (err)
>>> +                             goto unlock;
>>> +             }
>>> +
>>> +             err = vma->ops->set_pages(vma);
>>> +             if (err)
>>> +                     goto unlock;
>>>        }
>>> +     atomic_inc(&vma->pages_count);
>>>    
>>> -     if ((bound & I915_VMA_BIND_MASK) == 0) {
>>> -             ret = i915_vma_insert(vma, size, alignment, flags);
>>> -             if (ret)
>>> -                     goto err_unpin;
>>> +unlock:
>>> +     mutex_unlock(&vma->pages_mutex);
>>> +
>>> +     return err;
>>> +}
>>> +
>>> +static void __vma_put_pages(struct i915_vma *vma, unsigned int count)
>>> +{
>>> +     mutex_lock_nested(&vma->pages_mutex, SINGLE_DEPTH_NESTING);
>>
>> Nesting annotation only needed on the put path? Comment to describe why
>> it is needed would be good.
> 
> It's an allocating mutex, so we can be called from underneath another
> vma->pages_mutex. Standard misery.
> 
>>> +     GEM_BUG_ON(atomic_read(&vma->pages_count) < count);
>>> +     if (atomic_sub_return(count, &vma->pages_count) == 0) {
>>> +             vma->ops->clear_pages(vma);
>>> +             GEM_BUG_ON(vma->pages);
>>> +             if (vma->obj)
>>> +                     i915_gem_object_unpin_pages(vma->obj);
>>>        }
>>> -     GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
>>> +     mutex_unlock(&vma->pages_mutex);
>>> +}
>>>    
>>> -     ret = i915_vma_bind(vma, vma->obj ? vma->obj->cache_level : 0, flags);
>>> -     if (ret)
>>> -             goto err_remove;
>>> +static void vma_put_pages(struct i915_vma *vma)
>>> +{
>>> +     if (atomic_add_unless(&vma->pages_count, -1, 1))
>>> +             return;
>>>    
>>> -     GEM_BUG_ON(!i915_vma_is_bound(vma, I915_VMA_BIND_MASK));
>>> +     __vma_put_pages(vma, 1);
>>> +}
>>> +
>>> +static void vma_unbind_pages(struct i915_vma *vma)
>>> +{
>>> +     unsigned int count;
>>> +
>>> +     lockdep_assert_held(&vma->vm->mutex);
>>> +
>>> +     count = atomic_read(&vma->pages_count);
>>> +     count >>= I915_VMA_PAGES_BIAS;
>>> +     GEM_BUG_ON(!count);
>>> +
>>> +     __vma_put_pages(vma, count | count << I915_VMA_PAGES_BIAS);
>>
>> I think we need documentation on various flags and possible states.
> 
> It literally is just a bias! (2 counters within one.)
> 
>>> +int i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
>>> +{
>>> +     struct i915_vma_work *work = NULL;
>>> +     unsigned int bound;
>>> +     int err;
>>> +
>>> +     BUILD_BUG_ON(PIN_GLOBAL != I915_VMA_GLOBAL_BIND);
>>> +     BUILD_BUG_ON(PIN_USER != I915_VMA_LOCAL_BIND);
>>> +
>>> +     GEM_BUG_ON(flags & PIN_UPDATE);
>>> +     GEM_BUG_ON(!(flags & (PIN_USER | PIN_GLOBAL)));
>>>    
>>> -     if ((bound ^ atomic_read(&vma->flags)) & I915_VMA_GLOBAL_BIND)
>>> -             __i915_vma_set_map_and_fenceable(vma);
>>> +     if (try_fast_pin(vma, flags & I915_VMA_BIND_MASK))
>>> +             return 0;
>>> +
>>> +     err = vma_get_pages(vma);
>>> +     if (err)
>>> +             return err;
>>> +
>>> +     if (flags & PIN_USER) {
>>> +             work = i915_vma_work();
>>> +             if (!work) {
>>> +                     err = -ENOMEM;
>>> +                     goto err_pages;
>>> +             }
>>> +     }
>>
>> Good place for a comment to explain why PIN_USER needs to go via worker
>> and the rest dont.
> 
> Probably will change if my plans for bsw/bxt come to fruition. And
> certainly will change again as we will need to pass down the fence...
> 
>>> +     err = mutex_lock_interruptible(&vma->vm->mutex);
>>> +     if (err)
>>> +             goto err_fence;
>>> +
>>> +     bound = atomic_read(&vma->flags);
>>> +     if (unlikely(bound & I915_VMA_ERROR)) {
>>> +             err = -ENOMEM;
>>> +             goto err_unlock;
>>> +     }
>>>    
>>> +     if (unlikely(!((bound + 1) & I915_VMA_PIN_MASK))) {
>>> +             err = -EAGAIN; /* pins are meant to be fairly temporary */
>>> +             goto err_unlock;
>>> +     }
>>
>> Did not get what this is?
> 
> The overflow test.
> 
>>> +     if (unlikely(!(flags & ~bound & I915_VMA_BIND_MASK))) {
>>> +             __i915_vma_pin(vma);
>>> +             goto err_unlock;
>>> +     }
>>
>> Or this. (Comment please.)
> 
> Already bound by another thread.
> 
>>> +
>>> +     err = i915_active_acquire(&vma->active);
>>
>> It's dropped somewhere to release it into normal flows?
> 
> Not many lines below before we exit. The active is acquired again for
> the async operation to avoid the early release (on the success path).
> 
>>> +     if (err)
>>> +             goto err_unlock;
>>> +
>>> +     if (!(bound & I915_VMA_BIND_MASK)) {
>>> +             err = i915_vma_insert(vma, size, alignment, flags);
>>> +             if (err)
>>> +                     goto err_active;
>>> +
>>> +             if (i915_is_ggtt(vma->vm))
>>> +                     __i915_vma_set_map_and_fenceable(vma);
>>> +     }
>>> +
>>> +     GEM_BUG_ON(!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) */
>>
>> s/bindings/pins/ ? I thought one vma is one binding by definition.. so I
>> am confused.
> 
> Oh no, that would be far too simple. On gen6-7 & bsw, the vma is aliased.
> One vma, two binding (PIN_USER -> ggtt->alias, PIN_GLOBAL -> ggtt).

Okay I am not familiar with that and that's a problem when reviewing this.

What fundamentally happens when someone tried to PIN_USER and so far vma 
had PIN_GLOBAL (or vice-versa) in aliasing ggtt world? I see that we go 
to the "slow" path. Just increases the pin count?

> 
>>> +     GEM_BUG_ON(bound + I915_VMA_PAGES_ACTIVE < bound);
>>> +     atomic_add(I915_VMA_PAGES_ACTIVE, &vma->pages_count);
>>> +     list_move_tail(&vma->vm_link, &vma->vm->bound_list);
>>> +
>>> +     __i915_vma_pin(vma);
>>> +     GEM_BUG_ON(!i915_vma_is_pinned(vma));
>>> +     GEM_BUG_ON(!i915_vma_is_bound(vma, flags));
>>>        GEM_BUG_ON(i915_vma_misplaced(vma, size, alignment, flags));
>>> -     return 0;
>>>    
>>>    err_remove:
>>> -     if ((bound & I915_VMA_BIND_MASK) == 0) {
>>> +     if (!i915_vma_is_bound(vma, I915_VMA_BIND_MASK))
>>>                i915_vma_remove(vma);
>>> -             GEM_BUG_ON(vma->pages);
>>> -             GEM_BUG_ON(atomic_read(&vma->flags) & I915_VMA_BIND_MASK);
>>> -     }
>>> -err_unpin:
>>> -     __i915_vma_unpin(vma);
>>> -     return ret;
>>> +err_active:
>>> +     i915_active_release(&vma->active);
>>> +err_unlock:
>>> +     mutex_unlock(&vma->vm->mutex);
>>> +err_fence:
>>> +     if (work)
>>> +             dma_fence_work_commit(&work->base);
>>> +err_pages:
>>> +     vma_put_pages(vma);
>>> +     return err;
>>>    }
> 
>>>    void i915_vma_parked(struct drm_i915_private *i915)
>>> @@ -831,12 +1011,32 @@ void i915_vma_parked(struct drm_i915_private *i915)
>>>    
>>>        spin_lock_irq(&i915->gt.closed_lock);
>>>        list_for_each_entry_safe(vma, next, &i915->gt.closed_vma, closed_link) {
>>> -             list_del_init(&vma->closed_link);
>>> +             struct drm_i915_gem_object *obj = vma->obj;
>>> +             struct i915_address_space *vm = vma->vm;
>>> +
>>> +             /* XXX All to avoid keeping a reference on i915_vma itself */
>>
>> Does XXX signify it is a temporary state of code, or just like a comment
>> but more important in some way?
> 
> It's a critique of the ugly code (the comment you asked for before) that
> is required because I didn't want to have to throw away the entire lmem
> object handling code.
> 
>>> @@ -976,7 +977,6 @@ static int igt_vma_remapped_gtt(void *arg)
>>>    
>>>    out:
>>>        intel_runtime_pm_put(&i915->runtime_pm, wakeref);
>>> -     mutex_unlock(&i915->drm.struct_mutex);
>>>        i915_gem_object_put(obj);
>>>    
>>>        return err;
>>>
>>
>> In general, even when I did not explicitly asked for a comment, places
>> where things weren't clear to me should be considered to have them added
>> and probably more.
> 
> How else do I know what isn't clear to you... ;)

Good, when can I expect a more annotated version?

Regards,

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

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

* Re: [PATCH 13/21] drm/i915: Pull i915_vma_pin under the vm->mutex
  2019-09-19 13:37       ` Tvrtko Ursulin
@ 2019-09-19 14:05         ` Chris Wilson
  0 siblings, 0 replies; 53+ messages in thread
From: Chris Wilson @ 2019-09-19 14:05 UTC (permalink / raw)
  To: Tvrtko Ursulin, intel-gfx

Quoting Tvrtko Ursulin (2019-09-19 14:37:01)
> 
> On 17/09/2019 19:56, Chris Wilson wrote:
> > Quoting Tvrtko Ursulin (2019-09-17 13:37:55)
> >> On 02/09/2019 05:02, Chris Wilson wrote:
> >>> +void i915_active_set_exclusive(struct i915_active *ref, struct dma_fence *f)
> >>> +{
> >>> +     GEM_BUG_ON(i915_active_is_idle(ref));
> >>> +
> >>> +     dma_fence_get(f);
> >>> +
> >>> +     rcu_read_lock();
> >>> +     if (rcu_access_pointer(ref->excl)) {
> >>> +             struct dma_fence *old;
> >>> +
> >>> +             old = dma_fence_get_rcu_safe(&ref->excl);
> >>> +             if (old) {
> >>> +                     if (dma_fence_remove_callback(old, &ref->excl_cb))
> >>> +                             atomic_dec(&ref->count);
> >>> +                     dma_fence_put(old);
> >>> +             }
> >>
> >> Put some commentary in describing the business done with ref->excl and
> >> callbacks. Or this goes away later?
> > 
> > void i915_active_set_exclusive(struct i915_active *ref, struct dma_fence *f)
> > {
> >          GEM_BUG_ON(i915_active_is_idle(ref));
> > 
> >          mutex_acquire(&ref->mutex.dep_map, 0, 0, _THIS_IP_);
> >          if (!__i915_active_fence_set(&ref->excl, f))
> >                  atomic_inc(&ref->count);
> >          mutex_release(&ref->mutex.dep_map, 0, _THIS_IP_);
> > }
> 
> What is this?

What is becomes in a couple of patches time, where the entire
i915_active is built out of the same i915_active_fence (wrapping the
dma_fence_cb), and so this exclusive slot becomes more clearly a
preallocated version of one of the nodes (and not a special case, except
for the special case that the order is given by the caller and not our
mutex, which is a bit of a nuisance I admit).

> >>> @@ -109,20 +121,31 @@ int i915_gem_object_unbind(struct drm_i915_gem_object *obj,
> >>>        LIST_HEAD(still_in_list);
> >>>        int ret = 0;
> >>>    
> >>> -     lockdep_assert_held(&obj->base.dev->struct_mutex);
> >>> -
> >>>        spin_lock(&obj->vma.lock);
> >>>        while (!ret && (vma = list_first_entry_or_null(&obj->vma.list,
> >>>                                                       struct i915_vma,
> >>>                                                       obj_link))) {
> >>> +             struct i915_address_space *vm = vma->vm;
> >>> +
> >>> +             ret = -EBUSY;
> >>> +             if (!i915_vm_tryopen(vm))
> >>> +                     break;
> >>
> >> This is some race between different paths of vm closing/destruction and
> >> vma cleanup? We need a comment about it somewhere.. Here I guess is a
> >> good place.
> > 
> > 4 different paths all fighting; and coordinate by preventing the other 2
> > destruction paths from running as they run, and spinlocks to prevent the
> > other.
> >   
> >> And why break and not continue, there will be a second call coming from
> >> somewhere when it fails?
> > 
> > It failed to unbind the object, so it would end up in a busy spin
> > preventing the actual vm release from removing the vma.
> 
> But it could proceed to unbind what can be unbound. Otherwise is it 
> guaranteed to be called again when it fails, so eventually it will clean 
> up everything?

We gain nothing by doing so, except unbind things that were in use. We
have to completely unbind the object for the callers to make progress.

> >>> +             work->vma = vma;
> >>> +             work->cache_level = cache_level;
> >>> +             work->flags = bind_flags | I915_VMA_ALLOC;
> >>> +
> >>> +             /*
> >>> +              * Note we only want to chain up to the migration fence on
> >>> +              * the pages (not the object itself). As we don't track that,
> >>> +              * yet, we have to use the exclusive fence instead.
> >>> +              *
> >>> +              * Also note that we do not want to track the async vma as
> >>> +              * part of the obj->resv->excl_fence as it only affects
> >>> +              * execution and not content or object's backing store lifetime.
> >>> +              */
> >>> +             GEM_BUG_ON(i915_active_has_exclusive(&vma->active));
> >>> +             i915_active_set_exclusive(&vma->active, &work->base.dma);
> >>
> >> Oh right, dma_fence_work since it's not a worker but callback on
> >> signalling the fence... From what context it gets called (holding any
> >> locks?) and which locks the callback can/will take?
> > 
> > dma_fence_work has a dma_fence_cb that schedules a work_stuct which
> > executes our function. (And it also has a dma-fence through which we can
> > signal the completion/error of that function.)
> 
> And this is to avoid some allocation somewhere?

The fence-work generally is to schedule some work that has to wait for a
set of fences and wants to signal fence upon completion. We are using it
here to do the allocation after we drop the mutex. (Which means we have
to protect that work itself from the shrinker, which is fun.)

> >>> +static bool try_fast_pin(struct i915_vma *vma, unsigned int flags)
> >>>    {
> >>> -     const unsigned int bound = atomic_read(&vma->flags);
> >>> -     int ret;
> >>> +     unsigned int bound;
> >>> +     bool pinned = true;
> >>>    
> >>> -     lockdep_assert_held(&vma->vm->i915->drm.struct_mutex);
> >>> -     GEM_BUG_ON((flags & (PIN_GLOBAL | PIN_USER)) == 0);
> >>> -     GEM_BUG_ON((flags & PIN_GLOBAL) && !i915_vma_is_ggtt(vma));
> >>> +     bound = atomic_read(&vma->flags);
> >>> +     do {
> >>> +             if (unlikely(flags & ~bound))
> >>> +                     return false;
> >>
> >> What is this checking for?
> > 
> > If the requested binding (flags) is already available (bound).
> > 
> >>> -     if (WARN_ON(bound & I915_VMA_PIN_OVERFLOW)) {
> >>> -             ret = -EBUSY;
> >>> -             goto err_unpin;
> >>> +             if (unlikely(bound & (I915_VMA_OVERFLOW | I915_VMA_ERROR)))
> >>> +                     return false;
> >>
> >> What will the caller try to do in these cases?
> > 
> > Not do the early return.
> 
> How it will recover from error or overflow was the question I was aiming 
> at. But that looks to have been before I figured out what do you really 
> mean by fast in this context. Fast/slow terminology is not the clearest 
> here but I have no better suggestions at the moment.

Any hard condition is punted to the caller.

> >>> +     if (err)
> >>> +             goto err_unlock;
> >>> +
> >>> +     if (!(bound & I915_VMA_BIND_MASK)) {
> >>> +             err = i915_vma_insert(vma, size, alignment, flags);
> >>> +             if (err)
> >>> +                     goto err_active;
> >>> +
> >>> +             if (i915_is_ggtt(vma->vm))
> >>> +                     __i915_vma_set_map_and_fenceable(vma);
> >>> +     }
> >>> +
> >>> +     GEM_BUG_ON(!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) */
> >>
> >> s/bindings/pins/ ? I thought one vma is one binding by definition.. so I
> >> am confused.
> > 
> > Oh no, that would be far too simple. On gen6-7 & bsw, the vma is aliased.
> > One vma, two binding (PIN_USER -> ggtt->alias, PIN_GLOBAL -> ggtt).
> 
> Okay I am not familiar with that and that's a problem when reviewing this.
> 
> What fundamentally happens when someone tried to PIN_USER and so far vma 
> had PIN_GLOBAL (or vice-versa) in aliasing ggtt world? I see that we go 
> to the "slow" path. Just increases the pin count?

We use the same vma location (so no need to do i915_vma_insert) but we
need to bind it into the new GTT (i.e. update all the PTEs in that GTT).

> >> In general, even when I did not explicitly asked for a comment, places
> >> where things weren't clear to me should be considered to have them added
> >> and probably more.
> > 
> > How else do I know what isn't clear to you... ;)
> 
> Good, when can I expect a more annotated version?

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

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

* Re: [PATCH 15/21] drm/i915: Coordinate i915_active with its own mutex
  2019-09-02  4:02 ` [PATCH 15/21] drm/i915: Coordinate i915_active with its own mutex Chris Wilson
@ 2019-09-20 16:14   ` Tvrtko Ursulin
  2019-09-20 16:32     ` Chris Wilson
  0 siblings, 1 reply; 53+ messages in thread
From: Tvrtko Ursulin @ 2019-09-20 16:14 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 02/09/2019 05:02, Chris Wilson wrote:
> Forgo the struct_mutex serialisation for i915_active, and interpose its
> own mutex handling for active/retire.
> 
> This is a multi-layered sleight-of-hand. First, we had to ensure that no
> active/retire callbacks accidentally inverted the mutex ordering rules,
> nor assumed that they were themselves serialised by struct_mutex. More
> challenging though, is the rule over updating elements of the active
> rbtree. Instead of the whole i915_active now being serialised by
> struct_mutex, allocations/rotations of the tree are serialised by the
> i915_active.mutex and individual nodes are serialised by the caller
> using the i915_timeline.mutex (we need to use nested spinlocks to
> interact with the dma_fence callback lists).
> 
> The pain point here is that instead of a single mutex around execbuf, we
> now have to take a mutex for active tracker (one for each vma, context,
> etc) and a couple of spinlocks for each fence update. The improvement in
> fine grained locking allowing for multiple concurrent clients
> (eventually!) should be worth it in typical loads.
> 
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> ---
>   .../gpu/drm/i915/display/intel_frontbuffer.c  |   2 +-
>   drivers/gpu/drm/i915/display/intel_overlay.c  |   5 +-
>   .../gpu/drm/i915/gem/i915_gem_client_blt.c    |   2 +-
>   drivers/gpu/drm/i915/gem/i915_gem_context.c   |   8 +-
>   .../gpu/drm/i915/gem/i915_gem_object_types.h  |   1 +
>   drivers/gpu/drm/i915/gem/i915_gem_pm.c        |   9 +-
>   drivers/gpu/drm/i915/gt/intel_context.c       |   6 +-
>   drivers/gpu/drm/i915/gt/intel_engine_pool.c   |   2 +-
>   drivers/gpu/drm/i915/gt/intel_engine_pool.h   |   2 +-
>   drivers/gpu/drm/i915/gt/intel_reset.c         |  10 +-
>   drivers/gpu/drm/i915/gt/intel_timeline.c      |   9 +-
>   .../gpu/drm/i915/gt/intel_timeline_types.h    |   2 +-
>   drivers/gpu/drm/i915/gt/selftest_context.c    |  16 +-
>   drivers/gpu/drm/i915/gt/selftest_lrc.c        |  10 +-
>   .../gpu/drm/i915/gt/selftests/mock_timeline.c |   2 +-
>   drivers/gpu/drm/i915/gvt/scheduler.c          |   3 -
>   drivers/gpu/drm/i915/i915_active.c            | 291 +++++++---------
>   drivers/gpu/drm/i915/i915_active.h            | 318 ++++--------------
>   drivers/gpu/drm/i915/i915_active_types.h      |  23 +-
>   drivers/gpu/drm/i915/i915_gem.c               |  42 ++-
>   drivers/gpu/drm/i915/i915_gem_gtt.c           |   3 +-
>   drivers/gpu/drm/i915/i915_gpu_error.c         |   4 +-
>   drivers/gpu/drm/i915/i915_request.c           |  39 +--
>   drivers/gpu/drm/i915/i915_request.h           |   1 -
>   drivers/gpu/drm/i915/i915_vma.c               |   8 +-
>   drivers/gpu/drm/i915/selftests/i915_active.c  |  36 +-
>   26 files changed, 274 insertions(+), 580 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/display/intel_frontbuffer.c b/drivers/gpu/drm/i915/display/intel_frontbuffer.c
> index 6428b8dd70d3..84b164f31895 100644
> --- a/drivers/gpu/drm/i915/display/intel_frontbuffer.c
> +++ b/drivers/gpu/drm/i915/display/intel_frontbuffer.c
> @@ -257,7 +257,7 @@ intel_frontbuffer_get(struct drm_i915_gem_object *obj)
>   	front->obj = obj;
>   	kref_init(&front->ref);
>   	atomic_set(&front->bits, 0);
> -	i915_active_init(i915, &front->write,
> +	i915_active_init(&front->write,
>   			 frontbuffer_active,
>   			 i915_active_may_sleep(frontbuffer_retire));
>   
> diff --git a/drivers/gpu/drm/i915/display/intel_overlay.c b/drivers/gpu/drm/i915/display/intel_overlay.c
> index 4f36557b3f3b..544e953342ea 100644
> --- a/drivers/gpu/drm/i915/display/intel_overlay.c
> +++ b/drivers/gpu/drm/i915/display/intel_overlay.c
> @@ -230,7 +230,7 @@ alloc_request(struct intel_overlay *overlay, void (*fn)(struct intel_overlay *))
>   	if (IS_ERR(rq))
>   		return rq;
>   
> -	err = i915_active_ref(&overlay->last_flip, rq->timeline, rq);
> +	err = i915_active_ref(&overlay->last_flip, rq->timeline, &rq->fence);
>   	if (err) {
>   		i915_request_add(rq);
>   		return ERR_PTR(err);
> @@ -1360,8 +1360,7 @@ void intel_overlay_setup(struct drm_i915_private *dev_priv)
>   	overlay->contrast = 75;
>   	overlay->saturation = 146;
>   
> -	i915_active_init(dev_priv,
> -			 &overlay->last_flip,
> +	i915_active_init(&overlay->last_flip,
>   			 NULL, intel_overlay_last_flip_retire);
>   
>   	ret = get_registers(overlay, OVERLAY_NEEDS_PHYSICAL(dev_priv));
> 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 9e72b42a86f5..ace50bb9ee1f 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_client_blt.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_client_blt.c
> @@ -160,7 +160,7 @@ static int move_to_active(struct i915_vma *vma, struct i915_request *rq)
>   	if (err)
>   		return err;
>   
> -	return i915_active_ref(&vma->active, rq->timeline, rq);
> +	return i915_active_ref(&vma->active, rq->timeline, &rq->fence);
>   }
>   
>   static void clear_pages_worker(struct work_struct *work)
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context.c b/drivers/gpu/drm/i915/gem/i915_gem_context.c
> index 4f303cb94698..b8ddcf7899a1 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_context.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_context.c
> @@ -868,20 +868,18 @@ static int context_barrier_task(struct i915_gem_context *ctx,
>   				void (*task)(void *data),
>   				void *data)
>   {
> -	struct drm_i915_private *i915 = ctx->i915;
>   	struct context_barrier_task *cb;
>   	struct i915_gem_engines_iter it;
>   	struct intel_context *ce;
>   	int err = 0;
>   
> -	lockdep_assert_held(&i915->drm.struct_mutex);
>   	GEM_BUG_ON(!task);
>   
>   	cb = kmalloc(sizeof(*cb), GFP_KERNEL);
>   	if (!cb)
>   		return -ENOMEM;
>   
> -	i915_active_init(i915, &cb->base, NULL, cb_retire);
> +	i915_active_init(&cb->base, NULL, cb_retire);
>   	err = i915_active_acquire(&cb->base);
>   	if (err) {
>   		kfree(cb);
> @@ -913,7 +911,9 @@ static int context_barrier_task(struct i915_gem_context *ctx,
>   		if (emit)
>   			err = emit(rq, data);
>   		if (err == 0)
> -			err = i915_active_ref(&cb->base, rq->timeline, rq);
> +			err = i915_active_ref(&cb->base,
> +					      rq->timeline,
> +					      &rq->fence);
>   
>   		i915_request_add(rq);
>   		if (err)
> 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 b0550727e69a..03e1e3206ab3 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_object_types.h
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_object_types.h
> @@ -8,6 +8,7 @@
>   #define __I915_GEM_OBJECT_TYPES_H__
>   
>   #include <drm/drm_gem.h>
> +#include <uapi/drm/i915_drm.h>
>   
>   #include "i915_active.h"
>   #include "i915_selftest.h"
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_pm.c b/drivers/gpu/drm/i915/gem/i915_gem_pm.c
> index 92e53c25424c..92558fa47108 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_pm.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_pm.c
> @@ -16,14 +16,11 @@ static void call_idle_barriers(struct intel_engine_cs *engine)
>   	struct llist_node *node, *next;
>   
>   	llist_for_each_safe(node, next, llist_del_all(&engine->barrier_tasks)) {
> -		struct i915_active_request *active =
> +		struct dma_fence_cb *cb =
>   			container_of((struct list_head *)node,
> -				     typeof(*active), link);
> +				     typeof(*cb), node);
>   
> -		INIT_LIST_HEAD(&active->link);
> -		RCU_INIT_POINTER(active->request, NULL);
> -
> -		active->retire(active, NULL);
> +		cb->func(NULL, cb);
>   	}
>   }
>   
> diff --git a/drivers/gpu/drm/i915/gt/intel_context.c b/drivers/gpu/drm/i915/gt/intel_context.c
> index ae7c2689ef30..57e13c6f59c5 100644
> --- a/drivers/gpu/drm/i915/gt/intel_context.c
> +++ b/drivers/gpu/drm/i915/gt/intel_context.c
> @@ -240,7 +240,7 @@ intel_context_init(struct intel_context *ce,
>   
>   	mutex_init(&ce->pin_mutex);
>   
> -	i915_active_init(ctx->i915, &ce->active,
> +	i915_active_init(&ce->active,
>   			 __intel_context_active, __intel_context_retire);
>   }
>   
> @@ -307,7 +307,7 @@ int intel_context_prepare_remote_request(struct intel_context *ce,
>   			return err;
>   
>   		/* Queue this switch after current activity by this context. */
> -		err = i915_active_request_set(&tl->last_request, rq);
> +		err = i915_active_fence_set(&tl->last_request, rq);
>   		mutex_unlock(&tl->mutex);
>   		if (err)
>   			return err;
> @@ -321,7 +321,7 @@ int intel_context_prepare_remote_request(struct intel_context *ce,
>   	 * words transfer the pinned ce object to tracked active request.
>   	 */
>   	GEM_BUG_ON(i915_active_is_idle(&ce->active));
> -	return i915_active_ref(&ce->active, rq->timeline, rq);
> +	return i915_active_ref(&ce->active, rq->timeline, &rq->fence);
>   }
>   
>   struct i915_request *intel_context_create_request(struct intel_context *ce)
> diff --git a/drivers/gpu/drm/i915/gt/intel_engine_pool.c b/drivers/gpu/drm/i915/gt/intel_engine_pool.c
> index 81fab101fdb4..3cdbd5f8b5be 100644
> --- a/drivers/gpu/drm/i915/gt/intel_engine_pool.c
> +++ b/drivers/gpu/drm/i915/gt/intel_engine_pool.c
> @@ -95,7 +95,7 @@ node_create(struct intel_engine_pool *pool, size_t sz)
>   		return ERR_PTR(-ENOMEM);
>   
>   	node->pool = pool;
> -	i915_active_init(engine->i915, &node->active, pool_active, pool_retire);
> +	i915_active_init(&node->active, pool_active, pool_retire);
>   
>   	obj = i915_gem_object_create_internal(engine->i915, sz);
>   	if (IS_ERR(obj)) {
> diff --git a/drivers/gpu/drm/i915/gt/intel_engine_pool.h b/drivers/gpu/drm/i915/gt/intel_engine_pool.h
> index 7e2123b33594..4b0f3ae0c7e2 100644
> --- a/drivers/gpu/drm/i915/gt/intel_engine_pool.h
> +++ b/drivers/gpu/drm/i915/gt/intel_engine_pool.h
> @@ -18,7 +18,7 @@ static inline int
>   intel_engine_pool_mark_active(struct intel_engine_pool_node *node,
>   			      struct i915_request *rq)
>   {
> -	return i915_active_ref(&node->active, rq->timeline, rq);
> +	return i915_active_ref(&node->active, rq->timeline, &rq->fence);
>   }
>   
>   static inline void
> diff --git a/drivers/gpu/drm/i915/gt/intel_reset.c b/drivers/gpu/drm/i915/gt/intel_reset.c
> index b9d84d52e986..4825c82aefee 100644
> --- a/drivers/gpu/drm/i915/gt/intel_reset.c
> +++ b/drivers/gpu/drm/i915/gt/intel_reset.c
> @@ -814,10 +814,10 @@ static bool __intel_gt_unset_wedged(struct intel_gt *gt)
>   	 */
>   	spin_lock_irqsave(&timelines->lock, flags);
>   	list_for_each_entry(tl, &timelines->active_list, link) {
> -		struct i915_request *rq;
> +		struct dma_fence *fence;
>   
> -		rq = i915_active_request_get_unlocked(&tl->last_request);
> -		if (!rq)
> +		fence = i915_active_fence_get(&tl->last_request);
> +		if (!fence)
>   			continue;
>   
>   		spin_unlock_irqrestore(&timelines->lock, flags);
> @@ -829,8 +829,8 @@ static bool __intel_gt_unset_wedged(struct intel_gt *gt)
>   		 * (I915_FENCE_TIMEOUT) so this wait should not be unbounded
>   		 * in the worst case.
>   		 */
> -		dma_fence_default_wait(&rq->fence, false, MAX_SCHEDULE_TIMEOUT);
> -		i915_request_put(rq);
> +		dma_fence_default_wait(fence, false, MAX_SCHEDULE_TIMEOUT);
> +		dma_fence_put(fence);
>   
>   		/* Restart iteration after droping lock */
>   		spin_lock_irqsave(&timelines->lock, flags);
> diff --git a/drivers/gpu/drm/i915/gt/intel_timeline.c b/drivers/gpu/drm/i915/gt/intel_timeline.c
> index d824bca43d55..75d896167cfb 100644
> --- a/drivers/gpu/drm/i915/gt/intel_timeline.c
> +++ b/drivers/gpu/drm/i915/gt/intel_timeline.c
> @@ -178,8 +178,7 @@ cacheline_alloc(struct intel_timeline_hwsp *hwsp, unsigned int cacheline)
>   	cl->hwsp = hwsp;
>   	cl->vaddr = page_pack_bits(vaddr, cacheline);
>   
> -	i915_active_init(hwsp->gt->i915, &cl->active,
> -			 __cacheline_active, __cacheline_retire);
> +	i915_active_init(&cl->active, __cacheline_active, __cacheline_retire);
>   
>   	return cl;
>   }
> @@ -255,7 +254,7 @@ int intel_timeline_init(struct intel_timeline *timeline,
>   
>   	mutex_init(&timeline->mutex);
>   
> -	INIT_ACTIVE_REQUEST(&timeline->last_request, &timeline->mutex);
> +	INIT_ACTIVE_FENCE(&timeline->last_request, &timeline->mutex);
>   	INIT_LIST_HEAD(&timeline->requests);
>   
>   	i915_syncmap_init(&timeline->sync);
> @@ -443,7 +442,7 @@ __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);
> +	err = i915_active_ref(&tl->hwsp_cacheline->active, tl, &rq->fence);
>   	if (err)
>   		goto err_cacheline;
>   
> @@ -494,7 +493,7 @@ int intel_timeline_get_seqno(struct intel_timeline *tl,
>   static int cacheline_ref(struct intel_timeline_cacheline *cl,
>   			 struct i915_request *rq)
>   {
> -	return i915_active_ref(&cl->active, rq->timeline, rq);
> +	return i915_active_ref(&cl->active, rq->timeline, &rq->fence);
>   }
>   
>   int intel_timeline_read_hwsp(struct i915_request *from,
> diff --git a/drivers/gpu/drm/i915/gt/intel_timeline_types.h b/drivers/gpu/drm/i915/gt/intel_timeline_types.h
> index 2b1baf2fcc8e..6d7ac129ce8a 100644
> --- a/drivers/gpu/drm/i915/gt/intel_timeline_types.h
> +++ b/drivers/gpu/drm/i915/gt/intel_timeline_types.h
> @@ -63,7 +63,7 @@ struct intel_timeline {
>   	 * the request using i915_active_request_get_request_rcu(), or hold the

Looks like a stale comment.

>   	 * struct_mutex.
>   	 */
> -	struct i915_active_request last_request;
> +	struct i915_active_fence last_request;

Worth renaming to last_fence now that request naming is otherwise gone 
from i915_active?

>   
>   	/**
>   	 * We track the most recent seqno that we wait on in every context so
> diff --git a/drivers/gpu/drm/i915/gt/selftest_context.c b/drivers/gpu/drm/i915/gt/selftest_context.c
> index 9d1ea26c7a2d..1420533e8fd5 100644
> --- a/drivers/gpu/drm/i915/gt/selftest_context.c
> +++ b/drivers/gpu/drm/i915/gt/selftest_context.c
> @@ -41,24 +41,20 @@ static int context_sync(struct intel_context *ce)
>   
>   	mutex_lock(&tl->mutex);
>   	do {
> -		struct i915_request *rq;
> +		struct dma_fence *fence;
>   		long timeout;
>   
> -		rcu_read_lock();
> -		rq = rcu_dereference(tl->last_request.request);
> -		if (rq)
> -			rq = i915_request_get_rcu(rq);
> -		rcu_read_unlock();
> -		if (!rq)
> +		fence = i915_active_fence_get(&tl->last_request);
> +		if (!fence)
>   			break;
>   
> -		timeout = i915_request_wait(rq, 0, HZ / 10);
> +		timeout = dma_fence_wait_timeout(fence, false, HZ / 10);
>   		if (timeout < 0)
>   			err = timeout;
>   		else
> -			i915_request_retire_upto(rq);
> +			i915_request_retire_upto(to_request(fence));
>   
> -		i915_request_put(rq);
> +		dma_fence_put(fence);
>   	} while (!err);
>   	mutex_unlock(&tl->mutex);
>   
> diff --git a/drivers/gpu/drm/i915/gt/selftest_lrc.c b/drivers/gpu/drm/i915/gt/selftest_lrc.c
> index d791158988d6..aca1b3a9c5de 100644
> --- a/drivers/gpu/drm/i915/gt/selftest_lrc.c
> +++ b/drivers/gpu/drm/i915/gt/selftest_lrc.c
> @@ -984,9 +984,13 @@ static struct i915_request *dummy_request(struct intel_engine_cs *engine)
>   	if (!rq)
>   		return NULL;
>   
> -	INIT_LIST_HEAD(&rq->active_list);
>   	rq->engine = engine;
>   
> +	spin_lock_init(&rq->lock);
> +	INIT_LIST_HEAD(&rq->fence.cb_list);
> +	rq->fence.lock = &rq->lock;
> +	rq->fence.ops = &i915_fence_ops;
> +
>   	i915_sched_node_init(&rq->sched);
>   
>   	/* mark this request as permanently incomplete */
> @@ -1079,8 +1083,8 @@ static int live_suppress_wait_preempt(void *arg)
>   				}
>   
>   				/* Disable NEWCLIENT promotion */
> -				__i915_active_request_set(&rq[i]->timeline->last_request,
> -							  dummy);
> +				__i915_active_fence_set(&rq[i]->timeline->last_request,
> +							&dummy->fence);
>   				i915_request_add(rq[i]);
>   			}
>   
> diff --git a/drivers/gpu/drm/i915/gt/selftests/mock_timeline.c b/drivers/gpu/drm/i915/gt/selftests/mock_timeline.c
> index 598170efcaf6..2a77c051f36a 100644
> --- a/drivers/gpu/drm/i915/gt/selftests/mock_timeline.c
> +++ b/drivers/gpu/drm/i915/gt/selftests/mock_timeline.c
> @@ -15,7 +15,7 @@ void mock_timeline_init(struct intel_timeline *timeline, u64 context)
>   
>   	mutex_init(&timeline->mutex);
>   
> -	INIT_ACTIVE_REQUEST(&timeline->last_request, &timeline->mutex);
> +	INIT_ACTIVE_FENCE(&timeline->last_request, &timeline->mutex);
>   	INIT_LIST_HEAD(&timeline->requests);
>   
>   	i915_syncmap_init(&timeline->sync);
> diff --git a/drivers/gpu/drm/i915/gvt/scheduler.c b/drivers/gpu/drm/i915/gvt/scheduler.c
> index 8940fa8d391a..6beb753b1ea1 100644
> --- a/drivers/gpu/drm/i915/gvt/scheduler.c
> +++ b/drivers/gpu/drm/i915/gvt/scheduler.c
> @@ -385,11 +385,8 @@ intel_gvt_workload_req_alloc(struct intel_vgpu_workload *workload)
>   {
>   	struct intel_vgpu *vgpu = workload->vgpu;
>   	struct intel_vgpu_submission *s = &vgpu->submission;
> -	struct drm_i915_private *dev_priv = vgpu->gvt->dev_priv;
>   	struct i915_request *rq;
>   
> -	lockdep_assert_held(&dev_priv->drm.struct_mutex);
> -
>   	if (workload->req)
>   		return 0;
>   
> diff --git a/drivers/gpu/drm/i915/i915_active.c b/drivers/gpu/drm/i915/i915_active.c
> index f82d6d931824..2e2ab8176620 100644
> --- a/drivers/gpu/drm/i915/i915_active.c
> +++ b/drivers/gpu/drm/i915/i915_active.c
> @@ -12,8 +12,6 @@
>   #include "i915_active.h"
>   #include "i915_globals.h"
>   
> -#define BKL(ref) (&(ref)->i915->drm.struct_mutex)
> -
>   /*
>    * Active refs memory management
>    *
> @@ -27,35 +25,35 @@ static struct i915_global_active {
>   } global;
>   
>   struct active_node {
> -	struct i915_active_request base;
> +	struct i915_active_fence base;
>   	struct i915_active *ref;
>   	struct rb_node node;
>   	u64 timeline;
>   };
>   
>   static inline struct active_node *
> -node_from_active(struct i915_active_request *active)
> +node_from_active(struct i915_active_fence *active)
>   {
>   	return container_of(active, struct active_node, base);
>   }
>   
>   #define take_preallocated_barriers(x) llist_del_all(&(x)->preallocated_barriers)
>   
> -static inline bool is_barrier(const struct i915_active_request *active)
> +static inline bool is_barrier(const struct i915_active_fence *active)
>   {
> -	return IS_ERR(rcu_access_pointer(active->request));
> +	return IS_ERR(rcu_access_pointer(active->fence));
>   }
>   
>   static inline struct llist_node *barrier_to_ll(struct active_node *node)
>   {
>   	GEM_BUG_ON(!is_barrier(&node->base));
> -	return (struct llist_node *)&node->base.link;
> +	return (struct llist_node *)&node->base.cb.node;
>   }
>   
>   static inline struct intel_engine_cs *
>   __barrier_to_engine(struct active_node *node)
>   {
> -	return (struct intel_engine_cs *)READ_ONCE(node->base.link.prev);
> +	return (struct intel_engine_cs *)READ_ONCE(node->base.cb.node.prev);
>   }
>   
>   static inline struct intel_engine_cs *
> @@ -68,7 +66,7 @@ barrier_to_engine(struct active_node *node)
>   static inline struct active_node *barrier_from_ll(struct llist_node *x)
>   {
>   	return container_of((struct list_head *)x,
> -			    struct active_node, base.link);
> +			    struct active_node, base.cb.node);
>   }
>   
>   #if IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM) && IS_ENABLED(CONFIG_DEBUG_OBJECTS)
> @@ -147,15 +145,18 @@ __active_retire(struct i915_active *ref)
>   	if (!retire)
>   		return;
>   
> -	GEM_BUG_ON(rcu_access_pointer(ref->excl));
> +	GEM_BUG_ON(rcu_access_pointer(ref->excl.fence));
>   	rbtree_postorder_for_each_entry_safe(it, n, &root, node) {
> -		GEM_BUG_ON(i915_active_request_isset(&it->base));
> +		GEM_BUG_ON(i915_active_fence_isset(&it->base));
>   		kmem_cache_free(global.slab_cache, it);
>   	}
>   
>   	/* After the final retire, the entire struct may be freed */
>   	if (ref->retire)
>   		ref->retire(ref);
> +
> +	/* ... except if you wait on it, you must manage your own references! */
> +	wake_up_var(ref);
>   }
>   
>   static void
> @@ -189,12 +190,20 @@ active_retire(struct i915_active *ref)
>   }
>   
>   static void
> -node_retire(struct i915_active_request *base, struct i915_request *rq)
> +node_retire(struct dma_fence *fence, struct dma_fence_cb *cb)
>   {
> -	active_retire(node_from_active(base)->ref);
> +	i915_active_fence_cb(fence, cb);
> +	active_retire(container_of(cb, struct active_node, base.cb)->ref);
>   }
>   
> -static struct i915_active_request *
> +static void
> +excl_retire(struct dma_fence *fence, struct dma_fence_cb *cb)
> +{
> +	i915_active_fence_cb(fence, cb);
> +	active_retire(container_of(cb, struct i915_active, excl.cb));
> +}
> +
> +static struct i915_active_fence *
>   active_instance(struct i915_active *ref, struct intel_timeline *tl)
>   {
>   	struct active_node *node, *prealloc;
> @@ -238,7 +247,7 @@ active_instance(struct i915_active *ref, struct intel_timeline *tl)
>   	}
>   
>   	node = prealloc;
> -	i915_active_request_init(&node->base, &tl->mutex, NULL, node_retire);
> +	__i915_active_fence_init(&node->base, &tl->mutex, NULL, node_retire);
>   	node->ref = ref;
>   	node->timeline = idx;
>   
> @@ -253,8 +262,7 @@ active_instance(struct i915_active *ref, struct intel_timeline *tl)
>   	return &node->base;
>   }
>   
> -void __i915_active_init(struct drm_i915_private *i915,
> -			struct i915_active *ref,
> +void __i915_active_init(struct i915_active *ref,
>   			int (*active)(struct i915_active *ref),
>   			void (*retire)(struct i915_active *ref),
>   			struct lock_class_key *key)
> @@ -263,19 +271,18 @@ void __i915_active_init(struct drm_i915_private *i915,
>   
>   	debug_active_init(ref);
>   
> -	ref->i915 = i915;
>   	ref->flags = 0;
>   	ref->active = active;
>   	ref->retire = ptr_unpack_bits(retire, &bits, 2);
>   	if (bits & I915_ACTIVE_MAY_SLEEP)
>   		ref->flags |= I915_ACTIVE_RETIRE_SLEEPS;
>   
> -	ref->excl = NULL;
>   	ref->tree = RB_ROOT;
>   	ref->cache = NULL;
>   	init_llist_head(&ref->preallocated_barriers);
>   	atomic_set(&ref->count, 0);
>   	__mutex_init(&ref->mutex, "i915_active", key);
> +	__i915_active_fence_init(&ref->excl, &ref->mutex, NULL, excl_retire);
>   	INIT_WORK(&ref->work, active_work);
>   }
>   
> @@ -329,9 +336,9 @@ __active_del_barrier(struct i915_active *ref, struct active_node *node)
>   
>   int i915_active_ref(struct i915_active *ref,
>   		    struct intel_timeline *tl,
> -		    struct i915_request *rq)
> +		    struct dma_fence *fence)
>   {
> -	struct i915_active_request *active;
> +	struct i915_active_fence *active;
>   	int err;
>   
>   	lockdep_assert_held(&tl->mutex);
> @@ -354,65 +361,38 @@ int i915_active_ref(struct i915_active *ref,
>   		 * request that we want to emit on the kernel_context.
>   		 */
>   		__active_del_barrier(ref, node_from_active(active));
> -		RCU_INIT_POINTER(active->request, NULL);
> -		INIT_LIST_HEAD(&active->link);
> -	} else {
> -		if (!i915_active_request_isset(active))
> -			atomic_inc(&ref->count);
> +		RCU_INIT_POINTER(active->fence, NULL);
> +		atomic_dec(&ref->count);
>   	}
> -	GEM_BUG_ON(!atomic_read(&ref->count));
> -	__i915_active_request_set(active, rq);
> +	if (!__i915_active_fence_set(active, fence))
> +		atomic_inc(&ref->count);
>   
>   out:
>   	i915_active_release(ref);
>   	return err;
>   }
>   
> -static void excl_cb(struct dma_fence *f, struct dma_fence_cb *cb)
> -{
> -	struct i915_active *ref = container_of(cb, typeof(*ref), excl_cb);
> -
> -	RCU_INIT_POINTER(ref->excl, NULL);
> -	dma_fence_put(f);
> -
> -	active_retire(ref);
> -}
> -
>   void i915_active_set_exclusive(struct i915_active *ref, struct dma_fence *f)
>   {
>   	GEM_BUG_ON(i915_active_is_idle(ref));
>   
> -	dma_fence_get(f);
> -
> -	rcu_read_lock();
> -	if (rcu_access_pointer(ref->excl)) {
> -		struct dma_fence *old;
> -
> -		old = dma_fence_get_rcu_safe(&ref->excl);
> -		if (old) {
> -			if (dma_fence_remove_callback(old, &ref->excl_cb))
> -				atomic_dec(&ref->count);
> -			dma_fence_put(old);
> -		}
> -	}
> -	rcu_read_unlock();
> -
> -	atomic_inc(&ref->count);
> -	rcu_assign_pointer(ref->excl, f);
> +	mutex_acquire(&ref->mutex.dep_map, 0, 0, _THIS_IP_);

This just lockdep annotation? Probably deserves a comment.

> +	if (!__i915_active_fence_set(&ref->excl, f))
> +		atomic_inc(&ref->count);

Refcount management is not better done from inside __i915_active_fence_set?

> +	mutex_release(&ref->mutex.dep_map, 0, _THIS_IP_);
> +}
>   
> -	if (dma_fence_add_callback(f, &ref->excl_cb, excl_cb)) {
> -		RCU_INIT_POINTER(ref->excl, NULL);
> -		atomic_dec(&ref->count);
> -		dma_fence_put(f);
> -	}
> +bool i915_active_acquire_if_busy(struct i915_active *ref)
> +{
> +	debug_active_assert(ref);
> +	return atomic_add_unless(&ref->count, 1, 0);
>   }
>   
>   int i915_active_acquire(struct i915_active *ref)
>   {
>   	int err;
>   
> -	debug_active_assert(ref);
> -	if (atomic_add_unless(&ref->count, 1, 0))
> +	if (i915_active_acquire_if_busy(ref))
>   		return 0;
>   
>   	err = mutex_lock_interruptible(&ref->mutex);
> @@ -437,121 +417,49 @@ void i915_active_release(struct i915_active *ref)
>   	active_retire(ref);
>   }
>   
> -static void __active_ungrab(struct i915_active *ref)
> -{
> -	clear_and_wake_up_bit(I915_ACTIVE_GRAB_BIT, &ref->flags);
> -}
> -
> -bool i915_active_trygrab(struct i915_active *ref)
> -{
> -	debug_active_assert(ref);
> -
> -	if (test_and_set_bit(I915_ACTIVE_GRAB_BIT, &ref->flags))
> -		return false;
> -
> -	if (!atomic_add_unless(&ref->count, 1, 0)) {
> -		__active_ungrab(ref);
> -		return false;
> -	}
> -
> -	return true;
> -}
> -
> -void i915_active_ungrab(struct i915_active *ref)
> -{
> -	GEM_BUG_ON(!test_bit(I915_ACTIVE_GRAB_BIT, &ref->flags));
> -
> -	active_retire(ref);
> -	__active_ungrab(ref);
> -}
> -
> -static int excl_wait(struct i915_active *ref)
> -{
> -	struct dma_fence *old;
> -	int err = 0;
> -
> -	if (!rcu_access_pointer(ref->excl))
> -		return 0;
> -
> -	rcu_read_lock();
> -	old = dma_fence_get_rcu_safe(&ref->excl);
> -	rcu_read_unlock();
> -	if (old) {
> -		err = dma_fence_wait(old, true);
> -		dma_fence_put(old);
> -	}
> -
> -	return err;
> -}
> -
>   int i915_active_wait(struct i915_active *ref)
>   {
>   	struct active_node *it, *n;
> -	int err;
> +	int err = 0;
>   
>   	might_sleep();
> -	might_lock(&ref->mutex);
>   
> -	if (i915_active_is_idle(ref))
> +	if (!i915_active_acquire_if_busy(ref))
>   		return 0;
>   
> -	err = mutex_lock_interruptible(&ref->mutex);
> -	if (err)
> -		return err;
> -
> -	if (!atomic_add_unless(&ref->count, 1, 0)) {
> -		mutex_unlock(&ref->mutex);
> -		return 0;
> -	}
> +	/* Flush lazy signals */
> +	rbtree_postorder_for_each_entry_safe(it, n, &ref->tree, node) {
> +		struct dma_fence *fence;
>   
> -	err = excl_wait(ref);
> -	if (err)
> -		goto out;
> +		if (is_barrier(&it->base)) /* unconnected idle barrier */
> +			continue;
>   
> -	rbtree_postorder_for_each_entry_safe(it, n, &ref->tree, node) {
> -		if (is_barrier(&it->base)) { /* unconnected idle-barrier */
> -			err = -EBUSY;
> -			break;
> +		fence = i915_active_fence_get(&it->base);
> +		if (fence) {
> +			dma_fence_enable_sw_signaling(fence);
> +			dma_fence_put(fence);
>   		}
> -
> -		err = i915_active_request_retire(&it->base, BKL(ref));
> -		if (err)
> -			break;
>   	}
> -
> -out:
> -	__active_retire(ref);
> +	/* Any fence added after the wait begins will not be auto-signaled */
> +	i915_active_release(ref);
>   	if (err)
>   		return err;
>   
> -	if (wait_on_bit(&ref->flags, I915_ACTIVE_GRAB_BIT, TASK_KILLABLE))
> +	if (wait_var_event_interruptible(ref, i915_active_is_idle(ref)))
>   		return -EINTR;
>   
> -	flush_work(&ref->work);
> -	if (!i915_active_is_idle(ref))
> -		return -EBUSY;
> -
>   	return 0;
>   }
>   
> -int i915_request_await_active_request(struct i915_request *rq,
> -				      struct i915_active_request *active)
> -{
> -	struct i915_request *barrier =
> -		i915_active_request_raw(active, &rq->i915->drm.struct_mutex);
> -
> -	return barrier ? i915_request_await_dma_fence(rq, &barrier->fence) : 0;
> -}
> -
>   int i915_request_await_active(struct i915_request *rq, struct i915_active *ref)
>   {
>   	int err = 0;
>   
> -	if (rcu_access_pointer(ref->excl)) {
> +	if (rcu_access_pointer(ref->excl.fence)) {
>   		struct dma_fence *fence;
>   
>   		rcu_read_lock();
> -		fence = dma_fence_get_rcu_safe(&ref->excl);
> +		fence = dma_fence_get_rcu_safe(&ref->excl.fence);
>   		rcu_read_unlock();
>   		if (fence) {
>   			err = i915_request_await_dma_fence(rq, fence);
> @@ -577,7 +485,7 @@ void i915_active_fini(struct i915_active *ref)
>   
>   static inline bool is_idle_barrier(struct active_node *node, u64 idx)
>   {
> -	return node->timeline == idx && !i915_active_request_isset(&node->base);
> +	return node->timeline == idx && !i915_active_fence_isset(&node->base);
>   }
>   
>   static struct active_node *reuse_idle_barrier(struct i915_active *ref, u64 idx)
> @@ -697,13 +605,13 @@ int i915_active_acquire_preallocate_barrier(struct i915_active *ref,
>   			node->base.lock =
>   				&engine->kernel_context->timeline->mutex;
>   #endif
> -			RCU_INIT_POINTER(node->base.request, NULL);
> -			node->base.retire = node_retire;
> +			RCU_INIT_POINTER(node->base.fence, NULL);
> +			node->base.cb.func = node_retire;
>   			node->timeline = idx;
>   			node->ref = ref;
>   		}
>   
> -		if (!i915_active_request_isset(&node->base)) {
> +		if (!i915_active_fence_isset(&node->base)) {
>   			/*
>   			 * Mark this as being *our* unconnected proto-node.
>   			 *
> @@ -713,8 +621,8 @@ int i915_active_acquire_preallocate_barrier(struct i915_active *ref,
>   			 * and then we can use the rb_node and list pointers
>   			 * for our tracking of the pending barrier.
>   			 */
> -			RCU_INIT_POINTER(node->base.request, ERR_PTR(-EAGAIN));
> -			node->base.link.prev = (void *)engine;
> +			RCU_INIT_POINTER(node->base.fence, ERR_PTR(-EAGAIN));
> +			node->base.cb.node.prev = (void *)engine;
>   			atomic_inc(&ref->count);
>   		}
>   
> @@ -781,25 +689,65 @@ void i915_request_add_active_barriers(struct i915_request *rq)
>   {
>   	struct intel_engine_cs *engine = rq->engine;
>   	struct llist_node *node, *next;
> +	unsigned long flags;
>   
>   	GEM_BUG_ON(intel_engine_is_virtual(engine));
>   	GEM_BUG_ON(rq->timeline != engine->kernel_context->timeline);
>   
> +	node = llist_del_all(&engine->barrier_tasks);
> +	if (!node)
> +		return;
>   	/*
>   	 * Attach the list of proto-fences to the in-flight request such
>   	 * that the parent i915_active will be released when this request
>   	 * is retired.
>   	 */
> -	llist_for_each_safe(node, next, llist_del_all(&engine->barrier_tasks)) {
> -		RCU_INIT_POINTER(barrier_from_ll(node)->base.request, rq);
> +	spin_lock_irqsave(&rq->lock, flags);
> +	llist_for_each_safe(node, next, node) {
> +		RCU_INIT_POINTER(barrier_from_ll(node)->base.fence, &rq->fence);
>   		smp_wmb(); /* serialise with reuse_idle_barrier */
> -		list_add_tail((struct list_head *)node, &rq->active_list);
> +		list_add_tail((struct list_head *)node, &rq->fence.cb_list);
> +	}
> +	spin_unlock_irqrestore(&rq->lock, flags);
> +}
> +
> +#if IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM)
> +#define active_is_held(active) lockdep_is_held((active)->lock)
> +#else
> +#define active_is_held(active) true
> +#endif
> +
> +struct dma_fence *
> +__i915_active_fence_set(struct i915_active_fence *active,
> +			struct dma_fence *fence)
> +{
> +	struct dma_fence *prev;
> +	unsigned long flags;
> +
> +	/* NB: updates must be serialised by an outer timeline mutex */
> +	spin_lock_irqsave(fence->lock, flags);
> +	GEM_BUG_ON(test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags));
> +
> +	prev = rcu_dereference_protected(active->fence, active_is_held(active));
> +	if (prev) {
> +		spin_lock_nested(prev->lock, SINGLE_DEPTH_NESTING);
> +		__list_del_entry(&active->cb.node);
> +		spin_unlock(prev->lock); /* serialise with prev->cb_list */
> +		prev = rcu_access_pointer(active->fence);

Why it is important to re-read active->fence and does it then need a 
comment?

How does it tie with i915_active->count refcounting which is done on the 
basis of return value from this function?

>   	}
> +
> +	rcu_assign_pointer(active->fence, fence);
> +	list_add_tail(&active->cb.node, &fence->cb_list);
> +
> +	spin_unlock_irqrestore(fence->lock, flags);
> +
> +	return prev;
>   }
>   
> -int i915_active_request_set(struct i915_active_request *active,
> -			    struct i915_request *rq)
> +int i915_active_fence_set(struct i915_active_fence *active,
> +			  struct i915_request *rq)
>   {
> +	struct dma_fence *fence;
>   	int err;
>   
>   #if IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM)
> @@ -807,18 +755,25 @@ int i915_active_request_set(struct i915_active_request *active,
>   #endif
>   
>   	/* Must maintain ordering wrt previous active requests */
> -	err = i915_request_await_active_request(rq, active);
> -	if (err)
> -		return err;
> +	rcu_read_lock();
> +	fence = __i915_active_fence_set(active, &rq->fence);
> +	if (fence)
> +		fence = dma_fence_get_rcu(fence); > +	rcu_read_unlock();
> +
> +	if (fence) {
> +		err = i915_request_await_dma_fence(rq, fence);
> +		dma_fence_put(fence);
> +		if (err)
> +			return err;
> +	}
>   
> -	__i915_active_request_set(active, rq);
>   	return 0;
>   }
>   
> -void i915_active_retire_noop(struct i915_active_request *active,
> -			     struct i915_request *request)
> +void i915_active_noop(struct dma_fence *fence, struct dma_fence_cb *cb)
>   {
> -	/* Space left intentionally blank */
> +	i915_active_fence_cb(fence, cb);
>   }
>   
>   #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
> diff --git a/drivers/gpu/drm/i915/i915_active.h b/drivers/gpu/drm/i915/i915_active.h
> index af3d536e26fd..e1912d539014 100644
> --- a/drivers/gpu/drm/i915/i915_active.h
> +++ b/drivers/gpu/drm/i915/i915_active.h
> @@ -10,7 +10,10 @@
>   #include <linux/lockdep.h>
>   
>   #include "i915_active_types.h"
> -#include "i915_request.h"
> +
> +struct i915_request;
> +struct intel_engine_cs;
> +struct intel_timeline;
>   
>   /*
>    * We treat requests as fences. This is not be to confused with our
> @@ -28,308 +31,108 @@
>    * write access so that we can perform concurrent read operations between
>    * the CPU and GPU engines, as well as waiting for all rendering to
>    * complete, or waiting for the last GPU user of a "fence register". The
> - * object then embeds a #i915_active_request to track the most recent (in
> + * object then embeds a #i915_active_fence to track the most recent (in
>    * retirement order) request relevant for the desired mode of access.
> - * The #i915_active_request is updated with i915_active_request_set() to
> + * The #i915_active_fence is updated with i915_active_fence_set() to
>    * track the most recent fence request, typically this is done as part of
>    * i915_vma_move_to_active().
>    *
> - * When the #i915_active_request completes (is retired), it will
> + * When the #i915_active_fence completes (is retired), it will
>    * signal its completion to the owner through a callback as well as mark
> - * itself as idle (i915_active_request.request == NULL). The owner
> + * itself as idle (i915_active_fence.request == NULL). The owner
>    * can then perform any action, such as delayed freeing of an active
>    * resource including itself.
>    */
>   
> -void i915_active_retire_noop(struct i915_active_request *active,
> -			     struct i915_request *request);
> +void i915_active_noop(struct dma_fence *fence, struct dma_fence_cb *cb);
>   
>   /**
> - * i915_active_request_init - prepares the activity tracker for use
> + * __i915_active_fence_init - prepares the activity tracker for use
>    * @active - the active tracker
> - * @rq - initial request to track, can be NULL
> + * @fence - initial fence to track, can be NULL
>    * @func - a callback when then the tracker is retired (becomes idle),
>    *         can be NULL
>    *
> - * i915_active_request_init() prepares the embedded @active struct for use as
> - * an activity tracker, that is for tracking the last known active request
> - * associated with it. When the last request becomes idle, when it is retired
> + * i915_active_fence_init() prepares the embedded @active struct for use as
> + * an activity tracker, that is for tracking the last known active fence
> + * associated with it. When the last fence becomes idle, when it is retired
>    * after completion, the optional callback @func is invoked.
>    */
>   static inline void
> -i915_active_request_init(struct i915_active_request *active,
> +__i915_active_fence_init(struct i915_active_fence *active,
>   			 struct mutex *lock,
> -			 struct i915_request *rq,
> -			 i915_active_retire_fn retire)
> +			 void *fence,
> +			 dma_fence_func_t fn)
>   {
> -	RCU_INIT_POINTER(active->request, rq);
> -	INIT_LIST_HEAD(&active->link);
> -	active->retire = retire ?: i915_active_retire_noop;
> +	RCU_INIT_POINTER(active->fence, fence);
> +	active->cb.func = fn ?: i915_active_noop;
>   #if IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM)
>   	active->lock = lock;
>   #endif
>   }
>   
> -#define INIT_ACTIVE_REQUEST(name, lock) \
> -	i915_active_request_init((name), (lock), NULL, NULL)
> -
> -/**
> - * i915_active_request_set - updates the tracker to watch the current request
> - * @active - the active tracker
> - * @request - the request to watch
> - *
> - * __i915_active_request_set() watches the given @request for completion. Whilst
> - * that @request is busy, the @active reports busy. When that @request is
> - * retired, the @active tracker is updated to report idle.
> - */
> -static inline void
> -__i915_active_request_set(struct i915_active_request *active,
> -			  struct i915_request *request)
> -{
> -#if IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM)
> -	lockdep_assert_held(active->lock);
> -#endif
> -	list_move(&active->link, &request->active_list);
> -	rcu_assign_pointer(active->request, request);
> -}
> +#define INIT_ACTIVE_FENCE(A, LOCK) \
> +	__i915_active_fence_init((A), (LOCK), NULL, NULL)
>   
> -int __must_check
> -i915_active_request_set(struct i915_active_request *active,
> -			struct i915_request *rq);
> +struct dma_fence *
> +__i915_active_fence_set(struct i915_active_fence *active,
> +			struct dma_fence *fence);
>   
>   /**
> - * i915_active_request_raw - return the active request
> + * i915_active_fence_set - updates the tracker to watch the current fence
>    * @active - the active tracker
> + * @rq - the request to watch
>    *
> - * i915_active_request_raw() returns the current request being tracked, or NULL.
> - * It does not obtain a reference on the request for the caller, so the caller
> - * must hold struct_mutex.
> + * i915_active_fence_set() watches the given @rq for completion. While
> + * that @rq is busy, the @active reports busy. When that @rq is signaled
> + * (or else retired) the @active tracker is updated to report idle.
>    */
> -static inline struct i915_request *
> -i915_active_request_raw(const struct i915_active_request *active,
> -			struct mutex *mutex)
> -{
> -	return rcu_dereference_protected(active->request,
> -					 lockdep_is_held(mutex));
> -}
> -
> -/**
> - * i915_active_request_peek - report the active request being monitored
> - * @active - the active tracker
> - *
> - * i915_active_request_peek() returns the current request being tracked if
> - * still active, or NULL. It does not obtain a reference on the request
> - * for the caller, so the caller must hold struct_mutex.
> - */
> -static inline struct i915_request *
> -i915_active_request_peek(const struct i915_active_request *active,
> -			 struct mutex *mutex)
> -{
> -	struct i915_request *request;
> -
> -	request = i915_active_request_raw(active, mutex);
> -	if (!request || i915_request_completed(request))
> -		return NULL;
> -
> -	return request;
> -}
> -
> -/**
> - * i915_active_request_get - return a reference to the active request
> - * @active - the active tracker
> - *
> - * i915_active_request_get() returns a reference to the active request, or NULL
> - * if the active tracker is idle. The caller must hold struct_mutex.
> - */
> -static inline struct i915_request *
> -i915_active_request_get(const struct i915_active_request *active,
> -			struct mutex *mutex)
> -{
> -	return i915_request_get(i915_active_request_peek(active, mutex));
> -}
> -
> -/**
> - * __i915_active_request_get_rcu - return a reference to the active request
> - * @active - the active tracker
> - *
> - * __i915_active_request_get() returns a reference to the active request,
> - * or NULL if the active tracker is idle. The caller must hold the RCU read
> - * lock, but the returned pointer is safe to use outside of RCU.
> - */
> -static inline struct i915_request *
> -__i915_active_request_get_rcu(const struct i915_active_request *active)
> -{
> -	/*
> -	 * Performing a lockless retrieval of the active request is super
> -	 * tricky. SLAB_TYPESAFE_BY_RCU merely guarantees that the backing
> -	 * slab of request objects will not be freed whilst we hold the
> -	 * RCU read lock. It does not guarantee that the request itself
> -	 * will not be freed and then *reused*. Viz,
> -	 *
> -	 * Thread A			Thread B
> -	 *
> -	 * rq = active.request
> -	 *				retire(rq) -> free(rq);
> -	 *				(rq is now first on the slab freelist)
> -	 *				active.request = NULL
> -	 *
> -	 *				rq = new submission on a new object
> -	 * ref(rq)
> -	 *
> -	 * To prevent the request from being reused whilst the caller
> -	 * uses it, we take a reference like normal. Whilst acquiring
> -	 * the reference we check that it is not in a destroyed state
> -	 * (refcnt == 0). That prevents the request being reallocated
> -	 * whilst the caller holds on to it. To check that the request
> -	 * was not reallocated as we acquired the reference we have to
> -	 * check that our request remains the active request across
> -	 * the lookup, in the same manner as a seqlock. The visibility
> -	 * of the pointer versus the reference counting is controlled
> -	 * by using RCU barriers (rcu_dereference and rcu_assign_pointer).
> -	 *
> -	 * In the middle of all that, we inspect whether the request is
> -	 * complete. Retiring is lazy so the request may be completed long
> -	 * before the active tracker is updated. Querying whether the
> -	 * request is complete is far cheaper (as it involves no locked
> -	 * instructions setting cachelines to exclusive) than acquiring
> -	 * the reference, so we do it first. The RCU read lock ensures the
> -	 * pointer dereference is valid, but does not ensure that the
> -	 * seqno nor HWS is the right one! However, if the request was
> -	 * reallocated, that means the active tracker's request was complete.
> -	 * If the new request is also complete, then both are and we can
> -	 * just report the active tracker is idle. If the new request is
> -	 * incomplete, then we acquire a reference on it and check that
> -	 * it remained the active request.
> -	 *
> -	 * It is then imperative that we do not zero the request on
> -	 * reallocation, so that we can chase the dangling pointers!
> -	 * See i915_request_alloc().
> -	 */
> -	do {
> -		struct i915_request *request;
> -
> -		request = rcu_dereference(active->request);
> -		if (!request || i915_request_completed(request))
> -			return NULL;
> -
> -		/*
> -		 * An especially silly compiler could decide to recompute the
> -		 * result of i915_request_completed, more specifically
> -		 * re-emit the load for request->fence.seqno. A race would catch
> -		 * a later seqno value, which could flip the result from true to
> -		 * false. Which means part of the instructions below might not
> -		 * be executed, while later on instructions are executed. Due to
> -		 * barriers within the refcounting the inconsistency can't reach
> -		 * past the call to i915_request_get_rcu, but not executing
> -		 * that while still executing i915_request_put() creates
> -		 * havoc enough.  Prevent this with a compiler barrier.
> -		 */
> -		barrier();
> -
> -		request = i915_request_get_rcu(request);
> -
> -		/*
> -		 * What stops the following rcu_access_pointer() from occurring
> -		 * before the above i915_request_get_rcu()? If we were
> -		 * to read the value before pausing to get the reference to
> -		 * the request, we may not notice a change in the active
> -		 * tracker.
> -		 *
> -		 * The rcu_access_pointer() is a mere compiler barrier, which
> -		 * means both the CPU and compiler are free to perform the
> -		 * memory read without constraint. The compiler only has to
> -		 * ensure that any operations after the rcu_access_pointer()
> -		 * occur afterwards in program order. This means the read may
> -		 * be performed earlier by an out-of-order CPU, or adventurous
> -		 * compiler.
> -		 *
> -		 * The atomic operation at the heart of
> -		 * i915_request_get_rcu(), see dma_fence_get_rcu(), is
> -		 * atomic_inc_not_zero() which is only a full memory barrier
> -		 * when successful. That is, if i915_request_get_rcu()
> -		 * returns the request (and so with the reference counted
> -		 * incremented) then the following read for rcu_access_pointer()
> -		 * must occur after the atomic operation and so confirm
> -		 * that this request is the one currently being tracked.
> -		 *
> -		 * The corresponding write barrier is part of
> -		 * rcu_assign_pointer().
> -		 */
> -		if (!request || request == rcu_access_pointer(active->request))
> -			return rcu_pointer_handoff(request);
> -
> -		i915_request_put(request);
> -	} while (1);
> -}
> -
> +int __must_check
> +i915_active_fence_set(struct i915_active_fence *active,
> +		      struct i915_request *rq);
>   /**
> - * i915_active_request_get_unlocked - return a reference to the active request
> + * i915_active_fence_get - return a reference to the active fence
>    * @active - the active tracker
>    *
> - * i915_active_request_get_unlocked() returns a reference to the active request,
> + * i915_active_fence_get() returns a reference to the active fence,
>    * or NULL if the active tracker is idle. The reference is obtained under RCU,
>    * so no locking is required by the caller.
>    *
> - * The reference should be freed with i915_request_put().
> + * The reference should be freed with dma_fence_put().
>    */
> -static inline struct i915_request *
> -i915_active_request_get_unlocked(const struct i915_active_request *active)
> +static inline struct dma_fence *
> +i915_active_fence_get(struct i915_active_fence *active)
>   {
> -	struct i915_request *request;
> +	struct dma_fence *fence;
>   
>   	rcu_read_lock();
> -	request = __i915_active_request_get_rcu(active);
> +	fence = dma_fence_get_rcu_safe(&active->fence);
>   	rcu_read_unlock();
>   
> -	return request;
> +	return fence;
>   }
>   
>   /**
> - * i915_active_request_isset - report whether the active tracker is assigned
> + * i915_active_fence_isset - report whether the active tracker is assigned
>    * @active - the active tracker
>    *
> - * i915_active_request_isset() returns true if the active tracker is currently
> - * assigned to a request. Due to the lazy retiring, that request may be idle
> + * i915_active_fence_isset() returns true if the active tracker is currently
> + * assigned to a fence. Due to the lazy retiring, that fence may be idle
>    * and this may report stale information.
>    */
>   static inline bool
> -i915_active_request_isset(const struct i915_active_request *active)
> +i915_active_fence_isset(const struct i915_active_fence *active)
>   {
> -	return rcu_access_pointer(active->request);
> +	return rcu_access_pointer(active->fence);
>   }
>   
> -/**
> - * i915_active_request_retire - waits until the request is retired
> - * @active - the active request on which to wait
> - *
> - * i915_active_request_retire() waits until the request is completed,
> - * and then ensures that at least the retirement handler for this
> - * @active tracker is called before returning. If the @active
> - * tracker is idle, the function returns immediately.
> - */
> -static inline int __must_check
> -i915_active_request_retire(struct i915_active_request *active,
> -			   struct mutex *mutex)
> +static inline void
> +i915_active_fence_cb(struct dma_fence *fence, struct dma_fence_cb *cb)
>   {
> -	struct i915_request *request;
> -	long ret;
> -
> -	request = i915_active_request_raw(active, mutex);
> -	if (!request)
> -		return 0;
> -
> -	ret = i915_request_wait(request,
> -				I915_WAIT_INTERRUPTIBLE,
> -				MAX_SCHEDULE_TIMEOUT);
> -	if (ret < 0)
> -		return ret;
> +	struct i915_active_fence *active =
> +		container_of(cb, typeof(*active), cb);
>   
> -	list_del_init(&active->link);
> -	RCU_INIT_POINTER(active->request, NULL);
> -
> -	active->retire(active, request);
> -
> -	return 0;
> +	RCU_INIT_POINTER(active->fence, NULL);
>   }
>   
>   /*
> @@ -358,41 +161,34 @@ i915_active_request_retire(struct i915_active_request *active,
>    * synchronisation.
>    */
>   
> -void __i915_active_init(struct drm_i915_private *i915,
> -			struct i915_active *ref,
> +void __i915_active_init(struct i915_active *ref,
>   			int (*active)(struct i915_active *ref),
>   			void (*retire)(struct i915_active *ref),
>   			struct lock_class_key *key);
> -#define i915_active_init(i915, ref, active, retire) do {		\
> +#define i915_active_init(ref, active, retire) do {		\
>   	static struct lock_class_key __key;				\
>   									\
> -	__i915_active_init(i915, ref, active, retire, &__key);		\
> +	__i915_active_init(ref, active, retire, &__key);		\
>   } while (0)
>   
>   int i915_active_ref(struct i915_active *ref,
>   		    struct intel_timeline *tl,
> -		    struct i915_request *rq);
> +		    struct dma_fence *fence);
>   
>   void i915_active_set_exclusive(struct i915_active *ref, struct dma_fence *f);
>   
>   static inline bool i915_active_has_exclusive(struct i915_active *ref)
>   {
> -	return rcu_access_pointer(ref->excl);
> +	return rcu_access_pointer(ref->excl.fence);
>   }
>   
>   int i915_active_wait(struct i915_active *ref);
>   
> -int i915_request_await_active(struct i915_request *rq,
> -			      struct i915_active *ref);
> -int i915_request_await_active_request(struct i915_request *rq,
> -				      struct i915_active_request *active);
> +int i915_request_await_active(struct i915_request *rq, struct i915_active *ref);
>   
>   int i915_active_acquire(struct i915_active *ref);
> +bool i915_active_acquire_if_busy(struct i915_active *ref);
>   void i915_active_release(struct i915_active *ref);
> -void __i915_active_release_nested(struct i915_active *ref, int subclass);
> -
> -bool i915_active_trygrab(struct i915_active *ref);
> -void i915_active_ungrab(struct i915_active *ref);
>   
>   static inline bool
>   i915_active_is_idle(const struct i915_active *ref)
> diff --git a/drivers/gpu/drm/i915/i915_active_types.h b/drivers/gpu/drm/i915/i915_active_types.h
> index 021167f0004d..d89a74c142c6 100644
> --- a/drivers/gpu/drm/i915/i915_active_types.h
> +++ b/drivers/gpu/drm/i915/i915_active_types.h
> @@ -17,17 +17,9 @@
>   
>   #include "i915_utils.h"
>   
> -struct drm_i915_private;
> -struct i915_active_request;
> -struct i915_request;
> -
> -typedef void (*i915_active_retire_fn)(struct i915_active_request *,
> -				      struct i915_request *);
> -
> -struct i915_active_request {
> -	struct i915_request __rcu *request;
> -	struct list_head link;
> -	i915_active_retire_fn retire;
> +struct i915_active_fence {
> +	struct dma_fence __rcu *fence;
> +	struct dma_fence_cb cb;
>   #if IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM)
>   	/*
>   	 * Incorporeal!
> @@ -53,20 +45,17 @@ struct active_node;
>   #define i915_active_may_sleep(fn) ptr_pack_bits(&(fn), I915_ACTIVE_MAY_SLEEP, 2)
>   
>   struct i915_active {
> -	struct drm_i915_private *i915;
> +	atomic_t count;
> +	struct mutex mutex;
>   
>   	struct active_node *cache;
>   	struct rb_root tree;
> -	struct mutex mutex;
> -	atomic_t count;
>   
>   	/* Preallocated "exclusive" node */
> -	struct dma_fence __rcu *excl;
> -	struct dma_fence_cb excl_cb;
> +	struct i915_active_fence excl;
>   
>   	unsigned long flags;
>   #define I915_ACTIVE_RETIRE_SLEEPS BIT(0)
> -#define I915_ACTIVE_GRAB_BIT 1
>   
>   	int (*active)(struct i915_active *ref);
>   	void (*retire)(struct i915_active *ref);
> diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
> index 3eed2efa8d13..1aadab1cdd24 100644
> --- a/drivers/gpu/drm/i915/i915_gem.c
> +++ b/drivers/gpu/drm/i915/i915_gem.c
> @@ -900,28 +900,38 @@ wait_for_timelines(struct drm_i915_private *i915,
>   
>   	spin_lock_irqsave(&timelines->lock, flags);
>   	list_for_each_entry(tl, &timelines->active_list, link) {
> -		struct i915_request *rq;
> +		struct dma_fence *fence;
>   
> -		rq = i915_active_request_get_unlocked(&tl->last_request);
> -		if (!rq)
> +		fence = i915_active_fence_get(&tl->last_request);
> +		if (!fence)
>   			continue;
>   
>   		spin_unlock_irqrestore(&timelines->lock, flags);
>   
> -		/*
> -		 * "Race-to-idle".
> -		 *
> -		 * Switching to the kernel context is often used a synchronous
> -		 * step prior to idling, e.g. in suspend for flushing all
> -		 * current operations to memory before sleeping. These we
> -		 * want to complete as quickly as possible to avoid prolonged
> -		 * stalls, so allow the gpu to boost to maximum clocks.
> -		 */
> -		if (wait & I915_WAIT_FOR_IDLE_BOOST)
> -			gen6_rps_boost(rq);
> +		if (!dma_fence_is_i915(fence)) {
> +			timeout = dma_fence_wait_timeout(fence,
> +							 flags & I915_WAIT_INTERRUPTIBLE,
> +							 timeout);

We did not have support for non-i915 fences before this patch right? 
Would it be better to split this out in this case?

> +		} else {
> +			struct i915_request *rq = to_request(fence);
> +
> +			/*
> +			 * "Race-to-idle".
> +			 *
> +			 * Switching to the kernel context is often used as
> +			 * a synchronous step prior to idling, e.g. in suspend
> +			 * for flushing all current operations to memory before
> +			 * sleeping. These we want to complete as quickly as
> +			 * possible to avoid prolonged stalls, so allow the gpu
> +			 * to boost to maximum clocks.
> +			 */
> +			if (flags & I915_WAIT_FOR_IDLE_BOOST)
> +				gen6_rps_boost(rq);
> +
> +			timeout = i915_request_wait(rq, flags, timeout);
> +		}
>   
> -		timeout = i915_request_wait(rq, wait, timeout);
> -		i915_request_put(rq);
> +		dma_fence_put(fence);
>   		if (timeout < 0)
>   			return timeout;
>   
> diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
> index b2e4827788fc..60676de059a7 100644
> --- a/drivers/gpu/drm/i915/i915_gem_gtt.c
> +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
> @@ -1858,7 +1858,6 @@ static const struct i915_vma_ops pd_vma_ops = {
>   
>   static struct i915_vma *pd_vma_create(struct gen6_ppgtt *ppgtt, int size)
>   {
> -	struct drm_i915_private *i915 = ppgtt->base.vm.i915;
>   	struct i915_ggtt *ggtt = ppgtt->base.vm.gt->ggtt;
>   	struct i915_vma *vma;
>   
> @@ -1869,7 +1868,7 @@ static struct i915_vma *pd_vma_create(struct gen6_ppgtt *ppgtt, int size)
>   	if (!vma)
>   		return ERR_PTR(-ENOMEM);
>   
> -	i915_active_init(i915, &vma->active, NULL, NULL);
> +	i915_active_init(&vma->active, NULL, NULL);
>   
>   	mutex_init(&vma->pages_mutex);
>   	vma->vm = i915_vm_get(&ggtt->vm);
> diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c
> index 3ccf7fd9307f..2c0071ff2d2d 100644
> --- a/drivers/gpu/drm/i915/i915_gpu_error.c
> +++ b/drivers/gpu/drm/i915/i915_gpu_error.c
> @@ -1292,7 +1292,7 @@ capture_vma(struct capture_vma *next,
>   	if (!c)
>   		return next;
>   
> -	if (!i915_active_trygrab(&vma->active)) {
> +	if (!i915_active_acquire_if_busy(&vma->active)) {
>   		kfree(c);
>   		return next;
>   	}
> @@ -1432,7 +1432,7 @@ gem_record_rings(struct i915_gpu_state *error, struct compress *compress)
>   			*this->slot =
>   				i915_error_object_create(i915, vma, compress);
>   
> -			i915_active_ungrab(&vma->active);
> +			i915_active_release(&vma->active);
>   			i915_vma_put(vma);
>   
>   			capture = this->next;
> diff --git a/drivers/gpu/drm/i915/i915_request.c b/drivers/gpu/drm/i915/i915_request.c
> index 754a78364a63..4ecfae143276 100644
> --- a/drivers/gpu/drm/i915/i915_request.c
> +++ b/drivers/gpu/drm/i915/i915_request.c
> @@ -197,9 +197,8 @@ static void free_capture_list(struct i915_request *request)
>   
>   static bool i915_request_retire(struct i915_request *rq)
>   {
> -	struct i915_active_request *active, *next;
> -
>   	lockdep_assert_held(&rq->timeline->mutex);
> +
>   	if (!i915_request_completed(rq))
>   		return false;
>   
> @@ -223,35 +222,6 @@ static bool i915_request_retire(struct i915_request *rq)
>   	GEM_BUG_ON(!list_is_first(&rq->link, &rq->timeline->requests));
>   	rq->ring->head = rq->postfix;
>   
> -	/*
> -	 * Walk through the active list, calling retire on each. This allows
> -	 * objects to track their GPU activity and mark themselves as idle
> -	 * when their *last* active request is completed (updating state
> -	 * tracking lists for eviction, active references for GEM, etc).
> -	 *
> -	 * As the ->retire() may free the node, we decouple it first and
> -	 * pass along the auxiliary information (to avoid dereferencing
> -	 * the node after the callback).
> -	 */
> -	list_for_each_entry_safe(active, next, &rq->active_list, link) {
> -		/*
> -		 * In microbenchmarks or focusing upon time inside the kernel,
> -		 * we may spend an inordinate amount of time simply handling
> -		 * the retirement of requests and processing their callbacks.
> -		 * Of which, this loop itself is particularly hot due to the
> -		 * cache misses when jumping around the list of
> -		 * i915_active_request.  So we try to keep this loop as
> -		 * streamlined as possible and also prefetch the next
> -		 * i915_active_request to try and hide the likely cache miss.
> -		 */
> -		prefetchw(next);
> -
> -		INIT_LIST_HEAD(&active->link);
> -		RCU_INIT_POINTER(active->request, NULL);
> -
> -		active->retire(active, rq);
> -	}
> -
>   	local_irq_disable();
>   
>   	/*
> @@ -664,7 +634,6 @@ __i915_request_create(struct intel_context *ce, gfp_t gfp)
>   	rq->flags = 0;
>   	rq->execution_mask = ALL_ENGINES;
>   
> -	INIT_LIST_HEAD(&rq->active_list);
>   	INIT_LIST_HEAD(&rq->execute_cb);
>   
>   	/*
> @@ -703,7 +672,6 @@ __i915_request_create(struct intel_context *ce, gfp_t gfp)
>   	ce->ring->emit = rq->head;
>   
>   	/* Make sure we didn't add ourselves to external state before freeing */
> -	GEM_BUG_ON(!list_empty(&rq->active_list));
>   	GEM_BUG_ON(!list_empty(&rq->sched.signalers_list));
>   	GEM_BUG_ON(!list_empty(&rq->sched.waiters_list));
>   
> @@ -1096,8 +1064,8 @@ __i915_request_add_to_timeline(struct i915_request *rq)
>   	 * precludes optimising to use semaphores serialisation of a single
>   	 * timeline across engines.
>   	 */
> -	prev = rcu_dereference_protected(timeline->last_request.request,
> -					 lockdep_is_held(&timeline->mutex));
> +	prev = to_request(__i915_active_fence_set(&timeline->last_request,
> +						  &rq->fence));
>   	if (prev && !i915_request_completed(prev)) {
>   		if (is_power_of_2(prev->engine->mask | rq->engine->mask))
>   			i915_sw_fence_await_sw_fence(&rq->submit,
> @@ -1122,7 +1090,6 @@ __i915_request_add_to_timeline(struct i915_request *rq)
>   	 * us, the timeline will hold its seqno which is later than ours.
>   	 */
>   	GEM_BUG_ON(timeline->seqno != rq->fence.seqno);
> -	__i915_active_request_set(&timeline->last_request, rq);
>   
>   	return prev;
>   }
> diff --git a/drivers/gpu/drm/i915/i915_request.h b/drivers/gpu/drm/i915/i915_request.h
> index 8ac6e1226a56..3251d2bdbeea 100644
> --- a/drivers/gpu/drm/i915/i915_request.h
> +++ b/drivers/gpu/drm/i915/i915_request.h
> @@ -211,7 +211,6 @@ struct i915_request {
>   	 * on the active_list (of their final request).
>   	 */
>   	struct i915_capture_list *capture_list;
> -	struct list_head active_list;
>   
>   	/** Time at which this request was emitted, in jiffies. */
>   	unsigned long emitted_jiffies;
> diff --git a/drivers/gpu/drm/i915/i915_vma.c b/drivers/gpu/drm/i915/i915_vma.c
> index 5ec8c970f9a2..2c703fd3c2b9 100644
> --- a/drivers/gpu/drm/i915/i915_vma.c
> +++ b/drivers/gpu/drm/i915/i915_vma.c
> @@ -120,8 +120,7 @@ vma_create(struct drm_i915_gem_object *obj,
>   	vma->size = obj->base.size;
>   	vma->display_alignment = I915_GTT_MIN_ALIGNMENT;
>   
> -	i915_active_init(vm->i915, &vma->active,
> -			 __i915_vma_active, __i915_vma_retire);
> +	i915_active_init(&vma->active, __i915_vma_active, __i915_vma_retire);
>   
>   	/* Declare ourselves safe for use inside shrinkers */
>   	if (IS_ENABLED(CONFIG_LOCKDEP)) {
> @@ -1100,7 +1099,7 @@ int i915_vma_move_to_active(struct i915_vma *vma,
>   	 * add the active reference first and queue for it to be dropped
>   	 * *last*.
>   	 */
> -	err = i915_active_ref(&vma->active, rq->timeline, rq);
> +	err = i915_active_ref(&vma->active, rq->timeline, &rq->fence);
>   	if (unlikely(err))
>   		return err;
>   
> @@ -1108,7 +1107,7 @@ int i915_vma_move_to_active(struct i915_vma *vma,
>   		if (intel_frontbuffer_invalidate(obj->frontbuffer, ORIGIN_CS))
>   			i915_active_ref(&obj->frontbuffer->write,
>   					rq->timeline,
> -					rq);
> +					&rq->fence);
>   
>   		dma_resv_add_excl_fence(vma->resv, &rq->fence);
>   		obj->write_domain = I915_GEM_DOMAIN_RENDER;
> @@ -1146,6 +1145,7 @@ int i915_vma_unbind(struct i915_vma *vma)
>   	if (ret)
>   		return ret;
>   
> +	GEM_BUG_ON(i915_vma_is_active(vma));
>   	if (i915_vma_is_pinned(vma)) {
>   		vma_print_allocator(vma, "is pinned");
>   		return -EBUSY;
> diff --git a/drivers/gpu/drm/i915/selftests/i915_active.c b/drivers/gpu/drm/i915/selftests/i915_active.c
> index d5ac9944d093..af5827aac7b2 100644
> --- a/drivers/gpu/drm/i915/selftests/i915_active.c
> +++ b/drivers/gpu/drm/i915/selftests/i915_active.c
> @@ -68,7 +68,7 @@ static struct live_active *__live_alloc(struct drm_i915_private *i915)
>   		return NULL;
>   
>   	kref_init(&active->ref);
> -	i915_active_init(i915, &active->base, __live_active, __live_retire);
> +	i915_active_init(&active->base, __live_active, __live_retire);
>   
>   	return active;
>   }
> @@ -110,7 +110,9 @@ __live_active_setup(struct drm_i915_private *i915)
>   						       submit,
>   						       GFP_KERNEL);
>   		if (err >= 0)
> -			err = i915_active_ref(&active->base, rq->timeline, rq);
> +			err = i915_active_ref(&active->base,
> +					      rq->timeline,
> +					      &rq->fence);
>   		i915_request_add(rq);
>   		if (err) {
>   			pr_err("Failed to track active ref!\n");
> @@ -146,19 +148,13 @@ static int live_active_wait(void *arg)
>   {
>   	struct drm_i915_private *i915 = arg;
>   	struct live_active *active;
> -	intel_wakeref_t wakeref;
>   	int err = 0;
>   
>   	/* Check that we get a callback when requests retire upon waiting */
>   
> -	mutex_lock(&i915->drm.struct_mutex);
> -	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
> -
>   	active = __live_active_setup(i915);
> -	if (IS_ERR(active)) {
> -		err = PTR_ERR(active);
> -		goto err;
> -	}
> +	if (IS_ERR(active))
> +		return PTR_ERR(active);
>   
>   	i915_active_wait(&active->base);
>   	if (!READ_ONCE(active->retired)) {
> @@ -168,11 +164,9 @@ static int live_active_wait(void *arg)
>   
>   	__live_put(active);
>   
> +	mutex_lock(&i915->drm.struct_mutex);
>   	if (igt_flush_test(i915, I915_WAIT_LOCKED))
>   		err = -EIO;
> -
> -err:
> -	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
>   	mutex_unlock(&i915->drm.struct_mutex);
>   
>   	return err;
> @@ -182,23 +176,19 @@ static int live_active_retire(void *arg)
>   {
>   	struct drm_i915_private *i915 = arg;
>   	struct live_active *active;
> -	intel_wakeref_t wakeref;
>   	int err = 0;
>   
>   	/* Check that we get a callback when requests are indirectly retired */
>   
> -	mutex_lock(&i915->drm.struct_mutex);
> -	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
> -
>   	active = __live_active_setup(i915);
> -	if (IS_ERR(active)) {
> -		err = PTR_ERR(active);
> -		goto err;
> -	}
> +	if (IS_ERR(active))
> +		return PTR_ERR(active);
>   
>   	/* waits for & retires all requests */
> +	mutex_lock(&i915->drm.struct_mutex);
>   	if (igt_flush_test(i915, I915_WAIT_LOCKED))
>   		err = -EIO;
> +	mutex_unlock(&i915->drm.struct_mutex);
>   
>   	if (!READ_ONCE(active->retired)) {
>   		pr_err("i915_active not retired after flushing!\n");
> @@ -207,10 +197,6 @@ static int live_active_retire(void *arg)
>   
>   	__live_put(active);
>   
> -err:
> -	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
> -	mutex_unlock(&i915->drm.struct_mutex);
> -
>   	return err;
>   }
>   
> 

I can't really grasp it from the diff. Will need to apply and try again. 
It removes a lot of code so bonus points for that. :)

Regards,

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

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

* Re: [PATCH 16/21] drm/i915: Move idle barrier cleanup into engine-pm
  2019-09-02  4:02 ` [PATCH 16/21] drm/i915: Move idle barrier cleanup into engine-pm Chris Wilson
@ 2019-09-20 16:18   ` Tvrtko Ursulin
  0 siblings, 0 replies; 53+ messages in thread
From: Tvrtko Ursulin @ 2019-09-20 16:18 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 02/09/2019 05:02, Chris Wilson wrote:
> Now that we now longer need to guarantee that the active callback is
> under the struct_mutex, we can lift it out of the i915_gem_park() and
> into the engine parking itself.
> 
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> ---
>   drivers/gpu/drm/i915/gem/i915_gem_pm.c    | 19 -------------------
>   drivers/gpu/drm/i915/gt/intel_engine_pm.c | 15 +++++++++++++++
>   drivers/gpu/drm/i915/i915_active.c        |  1 +
>   3 files changed, 16 insertions(+), 19 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_pm.c b/drivers/gpu/drm/i915/gem/i915_gem_pm.c
> index 92558fa47108..6e4cc177cc7a 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_pm.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_pm.c
> @@ -11,29 +11,10 @@
>   #include "i915_drv.h"
>   #include "i915_globals.h"
>   
> -static void call_idle_barriers(struct intel_engine_cs *engine)
> -{
> -	struct llist_node *node, *next;
> -
> -	llist_for_each_safe(node, next, llist_del_all(&engine->barrier_tasks)) {
> -		struct dma_fence_cb *cb =
> -			container_of((struct list_head *)node,
> -				     typeof(*cb), node);
> -
> -		cb->func(NULL, cb);
> -	}
> -}
> -
>   static void i915_gem_park(struct drm_i915_private *i915)
>   {
> -	struct intel_engine_cs *engine;
> -	enum intel_engine_id id;
> -
>   	lockdep_assert_held(&i915->drm.struct_mutex);
>   
> -	for_each_engine(engine, i915, id)
> -		call_idle_barriers(engine); /* cleanup after wedging */
> -
>   	i915_vma_parked(i915);
>   
>   	i915_globals_park();
> diff --git a/drivers/gpu/drm/i915/gt/intel_engine_pm.c b/drivers/gpu/drm/i915/gt/intel_engine_pm.c
> index 65b5ca74b394..472b2259f629 100644
> --- a/drivers/gpu/drm/i915/gt/intel_engine_pm.c
> +++ b/drivers/gpu/drm/i915/gt/intel_engine_pm.c
> @@ -123,6 +123,19 @@ static bool switch_to_kernel_context(struct intel_engine_cs *engine)
>   	return result;
>   }
>   
> +static void call_idle_barriers(struct intel_engine_cs *engine)
> +{
> +	struct llist_node *node, *next;
> +
> +	llist_for_each_safe(node, next, llist_del_all(&engine->barrier_tasks)) {
> +		struct dma_fence_cb *cb =
> +			container_of((struct list_head *)node,
> +				     typeof(*cb), node);
> +
> +		cb->func(NULL, cb);
> +	}
> +}
> +
>   static int __engine_park(struct intel_wakeref *wf)
>   {
>   	struct intel_engine_cs *engine =
> @@ -142,6 +155,8 @@ static int __engine_park(struct intel_wakeref *wf)
>   
>   	GEM_TRACE("%s\n", engine->name);
>   
> +	call_idle_barriers(engine); /* cleanup after wedging */
> +
>   	intel_engine_disarm_breadcrumbs(engine);
>   	intel_engine_pool_park(&engine->pool);
>   
> diff --git a/drivers/gpu/drm/i915/i915_active.c b/drivers/gpu/drm/i915/i915_active.c
> index 2e2ab8176620..dcf5bc1d87e6 100644
> --- a/drivers/gpu/drm/i915/i915_active.c
> +++ b/drivers/gpu/drm/i915/i915_active.c
> @@ -679,6 +679,7 @@ void i915_active_acquire_barrier(struct i915_active *ref)
>   		rb_link_node(&node->node, parent, p);
>   		rb_insert_color(&node->node, &ref->tree);
>   
> +		GEM_BUG_ON(!intel_engine_pm_is_awake(engine));
>   		llist_add(barrier_to_ll(node), &engine->barrier_tasks);
>   		intel_engine_pm_put(engine);
>   	}
> 

If the rest plays out this is simple. :)

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] 53+ messages in thread

* Re: [PATCH 12/21] drm/i915: Mark up address spaces that may need to allocate
  2019-09-02  4:02 ` [PATCH 12/21] drm/i915: Mark up address spaces that may need to allocate Chris Wilson
@ 2019-09-20 16:22   ` Tvrtko Ursulin
  2019-09-20 16:35     ` Chris Wilson
  0 siblings, 1 reply; 53+ messages in thread
From: Tvrtko Ursulin @ 2019-09-20 16:22 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 02/09/2019 05:02, Chris Wilson wrote:
> Since we cannot allocate underneath the vm->mutex (it is used in the
> direct-reclaim paths), we need to shift the allocations off into a
> mutexless worker with fence recursion prevention. To know when we need
> this protection, we mark up the address spaces that do allocate before
> insertion.
> 
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> ---
>   drivers/gpu/drm/i915/i915_gem_gtt.c | 3 +++
>   drivers/gpu/drm/i915/i915_gem_gtt.h | 2 ++
>   2 files changed, 5 insertions(+)
> 
> diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
> index 9095f017162e..56d27cf09a3d 100644
> --- a/drivers/gpu/drm/i915/i915_gem_gtt.c
> +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
> @@ -1500,6 +1500,7 @@ static struct i915_ppgtt *gen8_ppgtt_create(struct drm_i915_private *i915)
>   			goto err_free_pd;
>   	}
>   
> +	ppgtt->vm.bind_alloc = I915_VMA_LOCAL_BIND;

So this is re-using I915_VMA_LOCAL_BIND as a trick? Is it clear how that 
works from these call sites? Should it be called bind_alloc*s*? 
bind_allocates? Or be a boolean which is converted to a trick flag in 
i915_vma_bind where a comment can be put explaining the trick?

Regards,

Tvrtko

>   	ppgtt->vm.insert_entries = gen8_ppgtt_insert;
>   	ppgtt->vm.allocate_va_range = gen8_ppgtt_alloc;
>   	ppgtt->vm.clear_range = gen8_ppgtt_clear;
> @@ -1947,6 +1948,7 @@ static struct i915_ppgtt *gen6_ppgtt_create(struct drm_i915_private *i915)
>   	ppgtt_init(&ppgtt->base, &i915->gt);
>   	ppgtt->base.vm.top = 1;
>   
> +	ppgtt->base.vm.bind_alloc = 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;
> @@ -2578,6 +2580,7 @@ static int init_aliasing_ppgtt(struct i915_ggtt *ggtt)
>   		goto err_ppgtt;
>   
>   	ggtt->alias = ppgtt;
> +	ggtt->vm.bind_alloc |= ppgtt->vm.bind_alloc;
>   
>   	GEM_BUG_ON(ggtt->vm.vma_ops.bind_vma != ggtt_bind_vma);
>   	ggtt->vm.vma_ops.bind_vma = aliasing_gtt_bind_vma;
> diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.h b/drivers/gpu/drm/i915/i915_gem_gtt.h
> index 57d27898639a..007bdaf4ba00 100644
> --- a/drivers/gpu/drm/i915/i915_gem_gtt.h
> +++ b/drivers/gpu/drm/i915/i915_gem_gtt.h
> @@ -305,6 +305,8 @@ struct i915_address_space {
>   	u64 total;		/* size addr space maps (ex. 2GB for ggtt) */
>   	u64 reserved;		/* size addr space reserved */
>   
> +	unsigned int bind_alloc;
> +
>   	bool closed;
>   
>   	struct mutex mutex; /* protects vma and our lists */
> 
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* Re: [PATCH 15/21] drm/i915: Coordinate i915_active with its own mutex
  2019-09-20 16:14   ` Tvrtko Ursulin
@ 2019-09-20 16:32     ` Chris Wilson
  0 siblings, 0 replies; 53+ messages in thread
From: Chris Wilson @ 2019-09-20 16:32 UTC (permalink / raw)
  To: Tvrtko Ursulin, intel-gfx

Quoting Tvrtko Ursulin (2019-09-20 17:14:43)
> 
> On 02/09/2019 05:02, Chris Wilson wrote:
> > diff --git a/drivers/gpu/drm/i915/gt/intel_timeline_types.h b/drivers/gpu/drm/i915/gt/intel_timeline_types.h
> > index 2b1baf2fcc8e..6d7ac129ce8a 100644
> > --- a/drivers/gpu/drm/i915/gt/intel_timeline_types.h
> > +++ b/drivers/gpu/drm/i915/gt/intel_timeline_types.h
> > @@ -63,7 +63,7 @@ struct intel_timeline {
> >        * the request using i915_active_request_get_request_rcu(), or hold the
> 
> Looks like a stale comment.
> 
> >        * struct_mutex.
> >        */
> > -     struct i915_active_request last_request;
> > +     struct i915_active_fence last_request;
> 
> Worth renaming to last_fence now that request naming is otherwise gone 
> from i915_active?

Hmm, although i915_active is taking on more generic dma-fences, this is
still assumed to be a i915_request in a couple of places.

There's good arguments for either last_request or last_fence. If I throw
a GEM_BUG_ON(!is_request()) around, that probably swings the debate in
favour of last_request. At least tries to capture that we do assume in
some places this is i915_request.

> >   void i915_active_set_exclusive(struct i915_active *ref, struct dma_fence *f)
> >   {
> >       GEM_BUG_ON(i915_active_is_idle(ref));
> >   
> > -     dma_fence_get(f);
> > -
> > -     rcu_read_lock();
> > -     if (rcu_access_pointer(ref->excl)) {
> > -             struct dma_fence *old;
> > -
> > -             old = dma_fence_get_rcu_safe(&ref->excl);
> > -             if (old) {
> > -                     if (dma_fence_remove_callback(old, &ref->excl_cb))
> > -                             atomic_dec(&ref->count);
> > -                     dma_fence_put(old);
> > -             }
> > -     }
> > -     rcu_read_unlock();
> > -
> > -     atomic_inc(&ref->count);
> > -     rcu_assign_pointer(ref->excl, f);
> > +     mutex_acquire(&ref->mutex.dep_map, 0, 0, _THIS_IP_);
> 
> This just lockdep annotation? Probably deserves a comment.

Yup.

> 
> > +     if (!__i915_active_fence_set(&ref->excl, f))
> > +             atomic_inc(&ref->count);
> 
> Refcount management is not better done from inside __i915_active_fence_set?

No, The active counting is on i915_active; i915_active_fence is just a
component.

E.g. other users want to know the fence that used to be in the
i915_active_fence:

int i915_active_fence_set(struct i915_active_fence *active,
                          struct i915_request *rq)
{
        struct dma_fence *fence;
        int err;

#if IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM)
        lockdep_assert_held(active->lock);
#endif

        /* Must maintain ordering wrt previous active requests */
        rcu_read_lock();
        fence = __i915_active_fence_set(active, &rq->fence);
        if (fence)
                fence = dma_fence_get_rcu(fence);
        rcu_read_unlock();

        if (fence) {
                err = i915_request_await_dma_fence(rq, fence);
                dma_fence_put(fence);
                if (err)
                        return err;
        }

        return 0;
}

and similarly in __i915_request_add_to_timeline()

> > +struct dma_fence *
> > +__i915_active_fence_set(struct i915_active_fence *active,
> > +                     struct dma_fence *fence)
> > +{
> > +     struct dma_fence *prev;
> > +     unsigned long flags;
> > +
> > +     /* NB: updates must be serialised by an outer timeline mutex */
> > +     spin_lock_irqsave(fence->lock, flags);
> > +     GEM_BUG_ON(test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags));
> > +
> > +     prev = rcu_dereference_protected(active->fence, active_is_held(active));
> > +     if (prev) {
> > +             spin_lock_nested(prev->lock, SINGLE_DEPTH_NESTING);
> > +             __list_del_entry(&active->cb.node);
> > +             spin_unlock(prev->lock); /* serialise with prev->cb_list */
> > +             prev = rcu_access_pointer(active->fence);
> 
> Why it is important to re-read active->fence and does it then need a 
> comment?

Because we have to serialise with the prev->cb_list. ;)

The write to active->fence is made on signaling, and that is performed
under the prev->lock. So we have to take the prev->lock ourselves to
flush any concurrent callbacks before we know whether they ran (having
taken the lock, we remove the cb from the list and so now that if it
has not run, it can not run).

> How does it tie with i915_active->count refcounting which is done on the 
> basis of return value from this function?

Same as above, we have to flush the signal callback to determine whether
or not we need to increment the active count.

> > diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
> > index 3eed2efa8d13..1aadab1cdd24 100644
> > --- a/drivers/gpu/drm/i915/i915_gem.c
> > +++ b/drivers/gpu/drm/i915/i915_gem.c
> > @@ -900,28 +900,38 @@ wait_for_timelines(struct drm_i915_private *i915,
> >   
> >       spin_lock_irqsave(&timelines->lock, flags);
> >       list_for_each_entry(tl, &timelines->active_list, link) {
> > -             struct i915_request *rq;
> > +             struct dma_fence *fence;
> >   
> > -             rq = i915_active_request_get_unlocked(&tl->last_request);
> > -             if (!rq)
> > +             fence = i915_active_fence_get(&tl->last_request);
> > +             if (!fence)
> >                       continue;
> >   
> >               spin_unlock_irqrestore(&timelines->lock, flags);
> >   
> > -             /*
> > -              * "Race-to-idle".
> > -              *
> > -              * Switching to the kernel context is often used a synchronous
> > -              * step prior to idling, e.g. in suspend for flushing all
> > -              * current operations to memory before sleeping. These we
> > -              * want to complete as quickly as possible to avoid prolonged
> > -              * stalls, so allow the gpu to boost to maximum clocks.
> > -              */
> > -             if (wait & I915_WAIT_FOR_IDLE_BOOST)
> > -                     gen6_rps_boost(rq);
> > +             if (!dma_fence_is_i915(fence)) {
> > +                     timeout = dma_fence_wait_timeout(fence,
> > +                                                      flags & I915_WAIT_INTERRUPTIBLE,
> > +                                                      timeout);
> 
> We did not have support for non-i915 fences before this patch right? 
> Would it be better to split this out in this case?

Part of raison d'etre for the patch was to allow for i915_active to have
dma-fences, and to become less i915-specific.

It's trivial enough that in a couple of patches time you will forget
that we even thought about i915_requests here ;)
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* Re: [PATCH 12/21] drm/i915: Mark up address spaces that may need to allocate
  2019-09-20 16:22   ` Tvrtko Ursulin
@ 2019-09-20 16:35     ` Chris Wilson
  2019-09-23  8:10       ` Tvrtko Ursulin
  0 siblings, 1 reply; 53+ messages in thread
From: Chris Wilson @ 2019-09-20 16:35 UTC (permalink / raw)
  To: Tvrtko Ursulin, intel-gfx

Quoting Tvrtko Ursulin (2019-09-20 17:22:42)
> 
> On 02/09/2019 05:02, Chris Wilson wrote:
> > Since we cannot allocate underneath the vm->mutex (it is used in the
> > direct-reclaim paths), we need to shift the allocations off into a
> > mutexless worker with fence recursion prevention. To know when we need
> > this protection, we mark up the address spaces that do allocate before
> > insertion.
> > 
> > Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> > ---
> >   drivers/gpu/drm/i915/i915_gem_gtt.c | 3 +++
> >   drivers/gpu/drm/i915/i915_gem_gtt.h | 2 ++
> >   2 files changed, 5 insertions(+)
> > 
> > diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
> > index 9095f017162e..56d27cf09a3d 100644
> > --- a/drivers/gpu/drm/i915/i915_gem_gtt.c
> > +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
> > @@ -1500,6 +1500,7 @@ static struct i915_ppgtt *gen8_ppgtt_create(struct drm_i915_private *i915)
> >                       goto err_free_pd;
> >       }
> >   
> > +     ppgtt->vm.bind_alloc = I915_VMA_LOCAL_BIND;
> 
> So this is re-using I915_VMA_LOCAL_BIND as a trick? Is it clear how that 
> works from these call sites? Should it be called bind_alloc*s*? 
> bind_allocates? Or be a boolean which is converted to a trick flag in 
> i915_vma_bind where a comment can be put explaining the trick?

Is it a trick? We need to differentiate between requests for LOCAL_BIND,
GLOBAL_BIND, LOCAL_BIND | GLOBAL_BIND, for different types of vm. Then I
have a plan on using the worker for GLOBAL_BIND on bsw/bxt to defer the
stop_machine().
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* Re: [PATCH 12/21] drm/i915: Mark up address spaces that may need to allocate
  2019-09-20 16:35     ` Chris Wilson
@ 2019-09-23  8:10       ` Tvrtko Ursulin
  2019-09-25  8:23         ` Chris Wilson
  0 siblings, 1 reply; 53+ messages in thread
From: Tvrtko Ursulin @ 2019-09-23  8:10 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 20/09/2019 17:35, Chris Wilson wrote:
> Quoting Tvrtko Ursulin (2019-09-20 17:22:42)
>>
>> On 02/09/2019 05:02, Chris Wilson wrote:
>>> Since we cannot allocate underneath the vm->mutex (it is used in the
>>> direct-reclaim paths), we need to shift the allocations off into a
>>> mutexless worker with fence recursion prevention. To know when we need
>>> this protection, we mark up the address spaces that do allocate before
>>> insertion.
>>>
>>> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
>>> ---
>>>    drivers/gpu/drm/i915/i915_gem_gtt.c | 3 +++
>>>    drivers/gpu/drm/i915/i915_gem_gtt.h | 2 ++
>>>    2 files changed, 5 insertions(+)
>>>
>>> diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
>>> index 9095f017162e..56d27cf09a3d 100644
>>> --- a/drivers/gpu/drm/i915/i915_gem_gtt.c
>>> +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
>>> @@ -1500,6 +1500,7 @@ static struct i915_ppgtt *gen8_ppgtt_create(struct drm_i915_private *i915)
>>>                        goto err_free_pd;
>>>        }
>>>    
>>> +     ppgtt->vm.bind_alloc = I915_VMA_LOCAL_BIND;
>>
>> So this is re-using I915_VMA_LOCAL_BIND as a trick? Is it clear how that
>> works from these call sites? Should it be called bind_alloc*s*?
>> bind_allocates? Or be a boolean which is converted to a trick flag in
>> i915_vma_bind where a comment can be put explaining the trick?
> 
> Is it a trick? We need to differentiate between requests for LOCAL_BIND,
> GLOBAL_BIND, LOCAL_BIND | GLOBAL_BIND, for different types of vm. Then I
> have a plan on using the worker for GLOBAL_BIND on bsw/bxt to defer the
> stop_machine().

What's the connection between "mark up the address spaces that do 
allocate before insertion" and I915_VMA_LOCAL_BIND?

Regards,

Tvrtko

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

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

* Re: [PATCH 17/21] drm/i915: Drop struct_mutex from around i915_retire_requests()
  2019-09-02  4:02 ` [PATCH 17/21] drm/i915: Drop struct_mutex from around i915_retire_requests() Chris Wilson
@ 2019-09-24 15:25   ` Tvrtko Ursulin
  2019-09-25  8:43     ` Chris Wilson
  0 siblings, 1 reply; 53+ messages in thread
From: Tvrtko Ursulin @ 2019-09-24 15:25 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 02/09/2019 05:02, Chris Wilson wrote:
> We don't need to hold struct_mutex now for retiring requests, so drop it
> from i915_retire_requests() and i915_gem_wait_for_idle(), finally
> removing I915_WAIT_LOCKED for good.
> 
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> ---
>   .../gpu/drm/i915/gem/i915_gem_client_blt.c    |   7 +-
>   drivers/gpu/drm/i915/gem/i915_gem_context.c   |  20 +--
>   drivers/gpu/drm/i915/gem/i915_gem_pm.c        |  45 +++----
>   .../i915/gem/selftests/i915_gem_coherency.c   |  40 +++---
>   .../drm/i915/gem/selftests/i915_gem_context.c |   4 +-
>   .../drm/i915/gem/selftests/i915_gem_mman.c    |   6 +-
>   .../i915/gem/selftests/i915_gem_object_blt.c  |   4 -
>   drivers/gpu/drm/i915/gt/selftest_context.c    |   4 +-
>   drivers/gpu/drm/i915/gt/selftest_hangcheck.c  |  89 +++----------
>   drivers/gpu/drm/i915/gt/selftest_lrc.c        |  21 ++-
>   drivers/gpu/drm/i915/gt/selftest_timeline.c   |  91 ++++++-------
>   .../gpu/drm/i915/gt/selftest_workarounds.c    |   6 +-
>   drivers/gpu/drm/i915/i915_debugfs.c           |  42 ++----
>   drivers/gpu/drm/i915/i915_gem.c               |  19 ++-
>   drivers/gpu/drm/i915/i915_request.h           |   7 +-
>   drivers/gpu/drm/i915/selftests/i915_active.c  |   8 +-
>   .../gpu/drm/i915/selftests/i915_gem_evict.c   |   2 +-
>   drivers/gpu/drm/i915/selftests/i915_gem_gtt.c |   4 -
>   drivers/gpu/drm/i915/selftests/i915_request.c | 125 +++++-------------
>   .../gpu/drm/i915/selftests/i915_selftest.c    |   8 +-
>   drivers/gpu/drm/i915/selftests/i915_vma.c     |   4 -
>   .../gpu/drm/i915/selftests/igt_flush_test.c   |  30 ++---
>   .../gpu/drm/i915/selftests/igt_flush_test.h   |   2 +-
>   .../gpu/drm/i915/selftests/igt_live_test.c    |   9 +-
>   .../gpu/drm/i915/selftests/mock_gem_device.c  |   4 -
>   25 files changed, 191 insertions(+), 410 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 ace50bb9ee1f..cf2057e515af 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_client_blt.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_client_blt.c
> @@ -166,7 +166,6 @@ static int move_to_active(struct i915_vma *vma, struct i915_request *rq)
>   static void clear_pages_worker(struct work_struct *work)
>   {
>   	struct clear_pages_work *w = container_of(work, typeof(*w), work);
> -	struct drm_i915_private *i915 = w->ce->engine->i915;
>   	struct drm_i915_gem_object *obj = w->sleeve->vma->obj;
>   	struct i915_vma *vma = w->sleeve->vma;
>   	struct i915_request *rq;
> @@ -184,11 +183,9 @@ static void clear_pages_worker(struct work_struct *work)
>   	obj->read_domains = I915_GEM_GPU_DOMAINS;
>   	obj->write_domain = 0;
>   
> -	/* XXX: we need to kill this */
> -	mutex_lock(&i915->drm.struct_mutex);
>   	err = i915_vma_pin(vma, 0, 0, PIN_USER);
>   	if (unlikely(err))
> -		goto out_unlock;
> +		goto out_signal;
>   
>   	batch = intel_emit_vma_fill_blt(w->ce, vma, w->value);
>   	if (IS_ERR(batch)) {
> @@ -240,8 +237,6 @@ static void clear_pages_worker(struct work_struct *work)
>   	intel_emit_vma_release(w->ce, batch);
>   out_unpin:
>   	i915_vma_unpin(vma);
> -out_unlock:
> -	mutex_unlock(&i915->drm.struct_mutex);
>   out_signal:
>   	if (unlikely(err)) {
>   		dma_fence_set_error(&w->dma, err);
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context.c b/drivers/gpu/drm/i915/gem/i915_gem_context.c
> index b8ddcf7899a1..3452f1497094 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_context.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_context.c
> @@ -1161,8 +1161,7 @@ gen8_modify_rpcs(struct intel_context *ce, struct intel_sseu sseu)
>   }
>   
>   static int
> -__intel_context_reconfigure_sseu(struct intel_context *ce,
> -				 struct intel_sseu sseu)
> +intel_context_reconfigure_sseu(struct intel_context *ce, struct intel_sseu sseu)
>   {
>   	int ret;
>   
> @@ -1185,23 +1184,6 @@ __intel_context_reconfigure_sseu(struct intel_context *ce,
>   	return ret;
>   }
>   
> -static int
> -intel_context_reconfigure_sseu(struct intel_context *ce, struct intel_sseu sseu)
> -{
> -	struct drm_i915_private *i915 = ce->engine->i915;
> -	int ret;
> -
> -	ret = mutex_lock_interruptible(&i915->drm.struct_mutex);
> -	if (ret)
> -		return ret;
> -
> -	ret = __intel_context_reconfigure_sseu(ce, sseu);
> -
> -	mutex_unlock(&i915->drm.struct_mutex);
> -
> -	return ret;
> -}
> -
>   static int
>   user_to_context_sseu(struct drm_i915_private *i915,
>   		     const struct drm_i915_gem_context_param_sseu *user,
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_pm.c b/drivers/gpu/drm/i915/gem/i915_gem_pm.c
> index 6e4cc177cc7a..fec0b410d1d9 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_pm.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_pm.c
> @@ -48,11 +48,7 @@ static void retire_work_handler(struct work_struct *work)
>   	struct drm_i915_private *i915 =
>   		container_of(work, typeof(*i915), gem.retire_work.work);
>   
> -	/* Come back later if the device is busy... */
> -	if (mutex_trylock(&i915->drm.struct_mutex)) {
> -		i915_retire_requests(i915);
> -		mutex_unlock(&i915->drm.struct_mutex);
> -	}
> +	i915_retire_requests(i915);
>   
>   	queue_delayed_work(i915->wq,
>   			   &i915->gem.retire_work,
> @@ -86,26 +82,23 @@ static bool switch_to_kernel_context_sync(struct intel_gt *gt)
>   {
>   	bool result = !intel_gt_is_wedged(gt);
>   
> -	do {
> -		if (i915_gem_wait_for_idle(gt->i915,
> -					   I915_WAIT_LOCKED |
> -					   I915_WAIT_FOR_IDLE_BOOST,
> -					   I915_GEM_IDLE_TIMEOUT) == -ETIME) {
> -			/* XXX hide warning from gem_eio */
> -			if (i915_modparams.reset) {
> -				dev_err(gt->i915->drm.dev,
> -					"Failed to idle engines, declaring wedged!\n");
> -				GEM_TRACE_DUMP();
> -			}
> -
> -			/*
> -			 * Forcibly cancel outstanding work and leave
> -			 * the gpu quiet.
> -			 */
> -			intel_gt_set_wedged(gt);
> -			result = false;
> +	if (i915_gem_wait_for_idle(gt->i915,
> +				   I915_WAIT_FOR_IDLE_BOOST,
> +				   I915_GEM_IDLE_TIMEOUT) == -ETIME) {
> +		/* XXX hide warning from gem_eio */
> +		if (i915_modparams.reset) {
> +			dev_err(gt->i915->drm.dev,
> +				"Failed to idle engines, declaring wedged!\n");
> +			GEM_TRACE_DUMP();
>   		}
> -	} while (i915_retire_requests(gt->i915) && result);
> +
> +		/*
> +		 * Forcibly cancel outstanding work and leave
> +		 * the gpu quiet.
> +		 */
> +		intel_gt_set_wedged(gt);
> +		result = false;
> +	}
>   
>   	if (intel_gt_pm_wait_for_idle(gt))
>   		result = false;
> @@ -125,8 +118,6 @@ void i915_gem_suspend(struct drm_i915_private *i915)
>   	intel_wakeref_auto(&i915->ggtt.userfault_wakeref, 0);
>   	flush_workqueue(i915->wq);
>   
> -	mutex_lock(&i915->drm.struct_mutex);
> -
>   	/*
>   	 * We have to flush all the executing contexts to main memory so
>   	 * that they can saved in the hibernation image. To ensure the last
> @@ -138,8 +129,6 @@ void i915_gem_suspend(struct drm_i915_private *i915)
>   	 */
>   	switch_to_kernel_context_sync(&i915->gt);
>   
> -	mutex_unlock(&i915->drm.struct_mutex);
> -
>   	cancel_delayed_work_sync(&i915->gt.hangcheck.work);
>   
>   	i915_gem_drain_freed_objects(i915);
> 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 0ff7a89aadca..549810f70aeb 100644
> --- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_coherency.c
> +++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_coherency.c
> @@ -7,6 +7,7 @@
>   #include <linux/prime_numbers.h>
>   
>   #include "gt/intel_gt.h"
> +#include "gt/intel_gt_pm.h"
>   
>   #include "i915_selftest.h"
>   #include "selftests/i915_random.h"
> @@ -78,7 +79,7 @@ static int gtt_set(struct drm_i915_gem_object *obj,
>   {
>   	struct i915_vma *vma;
>   	u32 __iomem *map;
> -	int err;
> +	int err = 0;
>   
>   	i915_gem_object_lock(obj);
>   	err = i915_gem_object_set_to_gtt_domain(obj, true);
> @@ -90,15 +91,21 @@ static int gtt_set(struct drm_i915_gem_object *obj,
>   	if (IS_ERR(vma))
>   		return PTR_ERR(vma);
>   
> +	intel_gt_pm_get(vma->vm->gt);
> +
>   	map = i915_vma_pin_iomap(vma);
>   	i915_vma_unpin(vma);
> -	if (IS_ERR(map))
> -		return PTR_ERR(map);
> +	if (IS_ERR(map)) {
> +		err = PTR_ERR(map);
> +		goto out_rpm;
> +	}
>   
>   	iowrite32(v, &map[offset / sizeof(*map)]);
>   	i915_vma_unpin_iomap(vma);
>   
> -	return 0;
> +out_rpm:
> +	intel_gt_pm_put(vma->vm->gt);
> +	return err;
>   }
>   
>   static int gtt_get(struct drm_i915_gem_object *obj,
> @@ -107,7 +114,7 @@ static int gtt_get(struct drm_i915_gem_object *obj,
>   {
>   	struct i915_vma *vma;
>   	u32 __iomem *map;
> -	int err;
> +	int err = 0;
>   
>   	i915_gem_object_lock(obj);
>   	err = i915_gem_object_set_to_gtt_domain(obj, false);
> @@ -119,15 +126,21 @@ static int gtt_get(struct drm_i915_gem_object *obj,
>   	if (IS_ERR(vma))
>   		return PTR_ERR(vma);
>   
> +	intel_gt_pm_get(vma->vm->gt);
> +
>   	map = i915_vma_pin_iomap(vma);
>   	i915_vma_unpin(vma);
> -	if (IS_ERR(map))
> -		return PTR_ERR(map);
> +	if (IS_ERR(map)) {
> +		err = PTR_ERR(map);
> +		goto out_rpm;
> +	}
>   
>   	*v = ioread32(&map[offset / sizeof(*map)]);
>   	i915_vma_unpin_iomap(vma);
>   
> -	return 0;
> +out_rpm:
> +	intel_gt_pm_put(vma->vm->gt);
> +	return err;
>   }
>   
>   static int wc_set(struct drm_i915_gem_object *obj,
> @@ -280,7 +293,6 @@ static int igt_gem_coherency(void *arg)
>   	struct drm_i915_private *i915 = arg;
>   	const struct igt_coherency_mode *read, *write, *over;
>   	struct drm_i915_gem_object *obj;
> -	intel_wakeref_t wakeref;
>   	unsigned long count, n;
>   	u32 *offsets, *values;
>   	int err = 0;
> @@ -299,8 +311,6 @@ static int igt_gem_coherency(void *arg)
>   
>   	values = offsets + ncachelines;
>   
> -	mutex_lock(&i915->drm.struct_mutex);
> -	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
>   	for (over = igt_coherency_mode; over->name; over++) {
>   		if (!over->set)
>   			continue;
> @@ -326,7 +336,7 @@ static int igt_gem_coherency(void *arg)
>   					obj = i915_gem_object_create_internal(i915, PAGE_SIZE);
>   					if (IS_ERR(obj)) {
>   						err = PTR_ERR(obj);
> -						goto unlock;
> +						goto free;
>   					}
>   
>   					i915_random_reorder(offsets, ncachelines, &prng);
> @@ -377,15 +387,13 @@ static int igt_gem_coherency(void *arg)
>   			}
>   		}
>   	}
> -unlock:
> -	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
> -	mutex_unlock(&i915->drm.struct_mutex);
> +free:
>   	kfree(offsets);
>   	return err;
>   
>   put_object:
>   	i915_gem_object_put(obj);
> -	goto unlock;
> +	goto free;
>   }
>   
>   int i915_gem_coherency_live_selftests(struct drm_i915_private *i915)
> 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 aa67c02ba98c..b87e35a713b8 100644
> --- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c
> +++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c
> @@ -949,7 +949,7 @@ __sseu_test(const char *name,
>   	if (ret)
>   		return ret;
>   
> -	ret = __intel_context_reconfigure_sseu(ce, sseu);
> +	ret = intel_context_reconfigure_sseu(ce, sseu);
>   	if (ret)
>   		goto out_spin;
>   
> @@ -1053,7 +1053,7 @@ __igt_ctx_sseu(struct drm_i915_private *i915,
>   		goto out_fail;
>   
>   out_fail:
> -	if (igt_flush_test(i915, I915_WAIT_LOCKED))
> +	if (igt_flush_test(i915))
>   		ret = -EIO;
>   
>   	intel_context_unpin(ce);
> 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 9c217dfe96a9..39c01bc4eb51 100644
> --- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c
> +++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c
> @@ -393,12 +393,8 @@ static void disable_retire_worker(struct drm_i915_private *i915)
>   
>   static void restore_retire_worker(struct drm_i915_private *i915)
>   {
> +	igt_flush_test(i915);
>   	intel_gt_pm_put(&i915->gt);
> -
> -	mutex_lock(&i915->drm.struct_mutex);
> -	igt_flush_test(i915, I915_WAIT_LOCKED);
> -	mutex_unlock(&i915->drm.struct_mutex);
> -
>   	i915_gem_driver_register__shrinker(i915);
>   }
>   
> diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_object_blt.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_object_blt.c
> index c21d747e7d05..9ec55b3a3815 100644
> --- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_object_blt.c
> +++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_object_blt.c
> @@ -65,9 +65,7 @@ static int igt_fill_blt(void *arg)
>   		if (!(obj->cache_coherent & I915_BO_CACHE_COHERENT_FOR_WRITE))
>   			obj->cache_dirty = true;
>   
> -		mutex_lock(&i915->drm.struct_mutex);
>   		err = i915_gem_object_fill_blt(obj, ce, val);
> -		mutex_unlock(&i915->drm.struct_mutex);
>   		if (err)
>   			goto err_unpin;
>   
> @@ -166,9 +164,7 @@ static int igt_copy_blt(void *arg)
>   		if (!(dst->cache_coherent & I915_BO_CACHE_COHERENT_FOR_WRITE))
>   			dst->cache_dirty = true;
>   
> -		mutex_lock(&i915->drm.struct_mutex);
>   		err = i915_gem_object_copy_blt(src, dst, ce);
> -		mutex_unlock(&i915->drm.struct_mutex);
>   		if (err)
>   			goto err_unpin;
>   
> diff --git a/drivers/gpu/drm/i915/gt/selftest_context.c b/drivers/gpu/drm/i915/gt/selftest_context.c
> index 1420533e8fd5..883739354b07 100644
> --- a/drivers/gpu/drm/i915/gt/selftest_context.c
> +++ b/drivers/gpu/drm/i915/gt/selftest_context.c
> @@ -312,7 +312,7 @@ static int live_active_context(void *arg)
>   		if (err)
>   			break;
>   
> -		err = igt_flush_test(gt->i915, I915_WAIT_LOCKED);
> +		err = igt_flush_test(gt->i915);
>   		if (err)
>   			break;
>   	}
> @@ -425,7 +425,7 @@ static int live_remote_context(void *arg)
>   		if (err)
>   			break;
>   
> -		err = igt_flush_test(gt->i915, I915_WAIT_LOCKED);
> +		err = igt_flush_test(gt->i915);
>   		if (err)
>   			break;
>   	}
> diff --git a/drivers/gpu/drm/i915/gt/selftest_hangcheck.c b/drivers/gpu/drm/i915/gt/selftest_hangcheck.c
> index e53eea1050f8..fc4d02406536 100644
> --- a/drivers/gpu/drm/i915/gt/selftest_hangcheck.c
> +++ b/drivers/gpu/drm/i915/gt/selftest_hangcheck.c
> @@ -58,7 +58,9 @@ static int hang_init(struct hang *h, struct intel_gt *gt)
>   	memset(h, 0, sizeof(*h));
>   	h->gt = gt;
>   
> +	mutex_lock(&gt->i915->drm.struct_mutex);
>   	h->ctx = kernel_context(gt->i915);
> +	mutex_unlock(&gt->i915->drm.struct_mutex);
>   	if (IS_ERR(h->ctx))
>   		return PTR_ERR(h->ctx);
>   
> @@ -285,7 +287,7 @@ static void hang_fini(struct hang *h)
>   
>   	kernel_context_close(h->ctx);
>   
> -	igt_flush_test(h->gt->i915, I915_WAIT_LOCKED);
> +	igt_flush_test(h->gt->i915);
>   }
>   
>   static bool wait_until_running(struct hang *h, struct i915_request *rq)
> @@ -309,10 +311,9 @@ static int igt_hang_sanitycheck(void *arg)
>   
>   	/* Basic check that we can execute our hanging batch */
>   
> -	mutex_lock(&gt->i915->drm.struct_mutex);
>   	err = hang_init(&h, gt);
>   	if (err)
> -		goto unlock;
> +		return err;
>   
>   	for_each_engine(engine, gt->i915, id) {
>   		struct intel_wedge_me w;
> @@ -355,8 +356,6 @@ static int igt_hang_sanitycheck(void *arg)
>   
>   fini:
>   	hang_fini(&h);
> -unlock:
> -	mutex_unlock(&gt->i915->drm.struct_mutex);
>   	return err;
>   }
>   
> @@ -395,8 +394,6 @@ static int igt_reset_nop(void *arg)
>   	reset_count = i915_reset_count(global);
>   	count = 0;
>   	do {
> -		mutex_lock(&gt->i915->drm.struct_mutex);
> -
>   		for_each_engine(engine, gt->i915, id) {
>   			int i;
>   
> @@ -417,7 +414,6 @@ static int igt_reset_nop(void *arg)
>   		intel_gt_reset(gt, ALL_ENGINES, NULL);
>   		igt_global_reset_unlock(gt);
>   
> -		mutex_unlock(&gt->i915->drm.struct_mutex);
>   		if (intel_gt_is_wedged(gt)) {
>   			err = -EIO;
>   			break;
> @@ -429,16 +425,13 @@ static int igt_reset_nop(void *arg)
>   			break;
>   		}
>   
> -		err = igt_flush_test(gt->i915, 0);
> +		err = igt_flush_test(gt->i915);
>   		if (err)
>   			break;
>   	} while (time_before(jiffies, end_time));
>   	pr_info("%s: %d resets\n", __func__, count);
>   
> -	mutex_lock(&gt->i915->drm.struct_mutex);
> -	err = igt_flush_test(gt->i915, I915_WAIT_LOCKED);
> -	mutex_unlock(&gt->i915->drm.struct_mutex);
> -
> +	err = igt_flush_test(gt->i915);
>   out:
>   	mock_file_free(gt->i915, file);
>   	if (intel_gt_is_wedged(gt))
> @@ -494,7 +487,6 @@ static int igt_reset_nop_engine(void *arg)
>   				break;
>   			}
>   
> -			mutex_lock(&gt->i915->drm.struct_mutex);
>   			for (i = 0; i < 16; i++) {
>   				struct i915_request *rq;
>   
> @@ -507,7 +499,6 @@ static int igt_reset_nop_engine(void *arg)
>   				i915_request_add(rq);
>   			}
>   			err = intel_engine_reset(engine, NULL);
> -			mutex_unlock(&gt->i915->drm.struct_mutex);
>   			if (err) {
>   				pr_err("i915_reset_engine failed\n");
>   				break;
> @@ -533,15 +524,12 @@ static int igt_reset_nop_engine(void *arg)
>   		if (err)
>   			break;
>   
> -		err = igt_flush_test(gt->i915, 0);
> +		err = igt_flush_test(gt->i915);
>   		if (err)
>   			break;
>   	}
>   
> -	mutex_lock(&gt->i915->drm.struct_mutex);
> -	err = igt_flush_test(gt->i915, I915_WAIT_LOCKED);
> -	mutex_unlock(&gt->i915->drm.struct_mutex);
> -
> +	err = igt_flush_test(gt->i915);
>   out:
>   	mock_file_free(gt->i915, file);
>   	if (intel_gt_is_wedged(gt))
> @@ -563,9 +551,7 @@ static int __igt_reset_engine(struct intel_gt *gt, bool active)
>   		return 0;
>   
>   	if (active) {
> -		mutex_lock(&gt->i915->drm.struct_mutex);
>   		err = hang_init(&h, gt);
> -		mutex_unlock(&gt->i915->drm.struct_mutex);
>   		if (err)
>   			return err;
>   	}
> @@ -593,17 +579,14 @@ static int __igt_reset_engine(struct intel_gt *gt, bool active)
>   			if (active) {
>   				struct i915_request *rq;
>   
> -				mutex_lock(&gt->i915->drm.struct_mutex);
>   				rq = hang_create_request(&h, engine);
>   				if (IS_ERR(rq)) {
>   					err = PTR_ERR(rq);
> -					mutex_unlock(&gt->i915->drm.struct_mutex);
>   					break;
>   				}
>   
>   				i915_request_get(rq);
>   				i915_request_add(rq);
> -				mutex_unlock(&gt->i915->drm.struct_mutex);
>   
>   				if (!wait_until_running(&h, rq)) {
>   					struct drm_printer p = drm_info_printer(gt->i915->drm.dev);
> @@ -647,7 +630,7 @@ static int __igt_reset_engine(struct intel_gt *gt, bool active)
>   		if (err)
>   			break;
>   
> -		err = igt_flush_test(gt->i915, 0);
> +		err = igt_flush_test(gt->i915);
>   		if (err)
>   			break;
>   	}
> @@ -655,11 +638,8 @@ static int __igt_reset_engine(struct intel_gt *gt, bool active)
>   	if (intel_gt_is_wedged(gt))
>   		err = -EIO;
>   
> -	if (active) {
> -		mutex_lock(&gt->i915->drm.struct_mutex);
> +	if (active)
>   		hang_fini(&h);
> -		mutex_unlock(&gt->i915->drm.struct_mutex);
> -	}
>   
>   	return err;
>   }
> @@ -741,10 +721,8 @@ static int active_engine(void *data)
>   		struct i915_request *old = rq[idx];
>   		struct i915_request *new;
>   
> -		mutex_lock(&engine->i915->drm.struct_mutex);
>   		new = igt_request_alloc(ctx[idx], engine);
>   		if (IS_ERR(new)) {
> -			mutex_unlock(&engine->i915->drm.struct_mutex);
>   			err = PTR_ERR(new);
>   			break;
>   		}
> @@ -755,7 +733,6 @@ static int active_engine(void *data)
>   
>   		rq[idx] = i915_request_get(new);
>   		i915_request_add(new);
> -		mutex_unlock(&engine->i915->drm.struct_mutex);
>   
>   		err = active_request_put(old);
>   		if (err)
> @@ -795,9 +772,7 @@ static int __igt_reset_engines(struct intel_gt *gt,
>   		return 0;
>   
>   	if (flags & TEST_ACTIVE) {
> -		mutex_lock(&gt->i915->drm.struct_mutex);
>   		err = hang_init(&h, gt);
> -		mutex_unlock(&gt->i915->drm.struct_mutex);
>   		if (err)
>   			return err;
>   
> @@ -855,17 +830,14 @@ static int __igt_reset_engines(struct intel_gt *gt,
>   			struct i915_request *rq = NULL;
>   
>   			if (flags & TEST_ACTIVE) {
> -				mutex_lock(&gt->i915->drm.struct_mutex);
>   				rq = hang_create_request(&h, engine);
>   				if (IS_ERR(rq)) {
>   					err = PTR_ERR(rq);
> -					mutex_unlock(&gt->i915->drm.struct_mutex);
>   					break;
>   				}
>   
>   				i915_request_get(rq);
>   				i915_request_add(rq);
> -				mutex_unlock(&gt->i915->drm.struct_mutex);
>   
>   				if (!wait_until_running(&h, rq)) {
>   					struct drm_printer p = drm_info_printer(gt->i915->drm.dev);
> @@ -977,9 +949,7 @@ static int __igt_reset_engines(struct intel_gt *gt,
>   		if (err)
>   			break;
>   
> -		mutex_lock(&gt->i915->drm.struct_mutex);
> -		err = igt_flush_test(gt->i915, I915_WAIT_LOCKED);
> -		mutex_unlock(&gt->i915->drm.struct_mutex);
> +		err = igt_flush_test(gt->i915);
>   		if (err)
>   			break;
>   	}
> @@ -987,11 +957,8 @@ static int __igt_reset_engines(struct intel_gt *gt,
>   	if (intel_gt_is_wedged(gt))
>   		err = -EIO;
>   
> -	if (flags & TEST_ACTIVE) {
> -		mutex_lock(&gt->i915->drm.struct_mutex);
> +	if (flags & TEST_ACTIVE)
>   		hang_fini(&h);
> -		mutex_unlock(&gt->i915->drm.struct_mutex);
> -	}
>   
>   	return err;
>   }
> @@ -1061,7 +1028,6 @@ static int igt_reset_wait(void *arg)
>   
>   	igt_global_reset_lock(gt);
>   
> -	mutex_lock(&gt->i915->drm.struct_mutex);
>   	err = hang_init(&h, gt);
>   	if (err)
>   		goto unlock;
> @@ -1109,7 +1075,6 @@ static int igt_reset_wait(void *arg)
>   fini:
>   	hang_fini(&h);
>   unlock:
> -	mutex_unlock(&gt->i915->drm.struct_mutex);
>   	igt_global_reset_unlock(gt);
>   
>   	if (intel_gt_is_wedged(gt))
> @@ -1189,10 +1154,9 @@ static int __igt_reset_evict_vma(struct intel_gt *gt,
>   
>   	/* Check that we can recover an unbind stuck on a hanging request */
>   
> -	mutex_lock(&gt->i915->drm.struct_mutex);
>   	err = hang_init(&h, gt);
>   	if (err)
> -		goto unlock;
> +		return err;
>   
>   	obj = i915_gem_object_create_internal(gt->i915, SZ_1M);
>   	if (IS_ERR(obj)) {
> @@ -1255,8 +1219,6 @@ static int __igt_reset_evict_vma(struct intel_gt *gt,
>   	if (err)
>   		goto out_rq;
>   
> -	mutex_unlock(&gt->i915->drm.struct_mutex);
> -
>   	if (!wait_until_running(&h, rq)) {
>   		struct drm_printer p = drm_info_printer(gt->i915->drm.dev);
>   
> @@ -1305,16 +1267,12 @@ static int __igt_reset_evict_vma(struct intel_gt *gt,
>   		put_task_struct(tsk);
>   	}
>   
> -	mutex_lock(&gt->i915->drm.struct_mutex);
>   out_rq:
>   	i915_request_put(rq);
>   out_obj:
>   	i915_gem_object_put(obj);
>   fini:
>   	hang_fini(&h);
> -unlock:
> -	mutex_unlock(&gt->i915->drm.struct_mutex);
> -
>   	if (intel_gt_is_wedged(gt))
>   		return -EIO;
>   
> @@ -1396,7 +1354,6 @@ static int igt_reset_queue(void *arg)
>   
>   	igt_global_reset_lock(gt);
>   
> -	mutex_lock(&gt->i915->drm.struct_mutex);
>   	err = hang_init(&h, gt);
>   	if (err)
>   		goto unlock;
> @@ -1511,7 +1468,7 @@ static int igt_reset_queue(void *arg)
>   
>   		i915_request_put(prev);
>   
> -		err = igt_flush_test(gt->i915, I915_WAIT_LOCKED);
> +		err = igt_flush_test(gt->i915);
>   		if (err)
>   			break;
>   	}
> @@ -1519,7 +1476,6 @@ static int igt_reset_queue(void *arg)
>   fini:
>   	hang_fini(&h);
>   unlock:
> -	mutex_unlock(&gt->i915->drm.struct_mutex);
>   	igt_global_reset_unlock(gt);
>   
>   	if (intel_gt_is_wedged(gt))
> @@ -1546,11 +1502,9 @@ static int igt_handle_error(void *arg)
>   	if (!engine || !intel_engine_can_store_dword(engine))
>   		return 0;
>   
> -	mutex_lock(&gt->i915->drm.struct_mutex);
> -
>   	err = hang_init(&h, gt);
>   	if (err)
> -		goto err_unlock;
> +		return err;
>   
>   	rq = hang_create_request(&h, engine);
>   	if (IS_ERR(rq)) {
> @@ -1574,8 +1528,6 @@ static int igt_handle_error(void *arg)
>   		goto err_request;
>   	}
>   
> -	mutex_unlock(&gt->i915->drm.struct_mutex);
> -
>   	/* Temporarily disable error capture */
>   	error = xchg(&global->first_error, (void *)-1);
>   
> @@ -1583,8 +1535,6 @@ static int igt_handle_error(void *arg)
>   
>   	xchg(&global->first_error, error);
>   
> -	mutex_lock(&gt->i915->drm.struct_mutex);
> -
>   	if (rq->fence.error != -EIO) {
>   		pr_err("Guilty request not identified!\n");
>   		err = -EINVAL;
> @@ -1595,8 +1545,6 @@ static int igt_handle_error(void *arg)
>   	i915_request_put(rq);
>   err_fini:
>   	hang_fini(&h);
> -err_unlock:
> -	mutex_unlock(&gt->i915->drm.struct_mutex);
>   	return err;
>   }
>   
> @@ -1689,7 +1637,6 @@ static int igt_reset_engines_atomic(void *arg)
>   		return 0;
>   
>   	igt_global_reset_lock(gt);
> -	mutex_lock(&gt->i915->drm.struct_mutex);
>   
>   	/* Flush any requests before we get started and check basics */
>   	if (!igt_force_reset(gt))
> @@ -1709,9 +1656,7 @@ static int igt_reset_engines_atomic(void *arg)
>   out:
>   	/* As we poke around the guts, do a full reset before continuing. */
>   	igt_force_reset(gt);
> -
>   unlock:
> -	mutex_unlock(&gt->i915->drm.struct_mutex);
>   	igt_global_reset_unlock(gt);
>   
>   	return err;
> @@ -1751,10 +1696,6 @@ int intel_hangcheck_live_selftests(struct drm_i915_private *i915)
>   
>   	err = intel_gt_live_subtests(tests, gt);
>   
> -	mutex_lock(&gt->i915->drm.struct_mutex);
> -	igt_flush_test(gt->i915, I915_WAIT_LOCKED);
> -	mutex_unlock(&gt->i915->drm.struct_mutex);
> -
>   	i915_modparams.enable_hangcheck = saved_hangcheck;
>   	intel_runtime_pm_put(&gt->i915->runtime_pm, wakeref);
>   
> diff --git a/drivers/gpu/drm/i915/gt/selftest_lrc.c b/drivers/gpu/drm/i915/gt/selftest_lrc.c
> index aca1b3a9c5de..222a7375c787 100644
> --- a/drivers/gpu/drm/i915/gt/selftest_lrc.c
> +++ b/drivers/gpu/drm/i915/gt/selftest_lrc.c
> @@ -61,7 +61,7 @@ static int live_sanitycheck(void *arg)
>   		}
>   
>   		igt_spinner_end(&spin);
> -		if (igt_flush_test(i915, I915_WAIT_LOCKED)) {
> +		if (igt_flush_test(i915)) {
>   			err = -EIO;
>   			goto err_ctx;
>   		}
> @@ -206,8 +206,7 @@ slice_semaphore_queue(struct intel_engine_cs *outer,
>   	if (err)
>   		goto out;
>   
> -	if (i915_request_wait(head,
> -			      I915_WAIT_LOCKED,
> +	if (i915_request_wait(head, 0,
>   			      2 * RUNTIME_INFO(outer->i915)->num_engines * (count + 2) * (count + 3)) < 0) {
>   		pr_err("Failed to slice along semaphore chain of length (%d, %d)!\n",
>   		       count, n);
> @@ -279,7 +278,7 @@ static int live_timeslice_preempt(void *arg)
>   			if (err)
>   				goto err_pin;
>   
> -			if (igt_flush_test(i915, I915_WAIT_LOCKED)) {
> +			if (igt_flush_test(i915)) {
>   				err = -EIO;
>   				goto err_pin;
>   			}
> @@ -832,7 +831,7 @@ static int live_nopreempt(void *arg)
>   			goto err_wedged;
>   		}
>   
> -		if (igt_flush_test(i915, I915_WAIT_LOCKED))
> +		if (igt_flush_test(i915))
>   			goto err_wedged;
>   	}
>   
> @@ -948,7 +947,7 @@ static int live_suppress_self_preempt(void *arg)
>   			goto err_client_b;
>   		}
>   
> -		if (igt_flush_test(i915, I915_WAIT_LOCKED))
> +		if (igt_flush_test(i915))
>   			goto err_wedged;
>   	}
>   
> @@ -1109,7 +1108,7 @@ static int live_suppress_wait_preempt(void *arg)
>   			for (i = 0; i < ARRAY_SIZE(client); i++)
>   				igt_spinner_end(&client[i].spin);
>   
> -			if (igt_flush_test(i915, I915_WAIT_LOCKED))
> +			if (igt_flush_test(i915))
>   				goto err_wedged;
>   
>   			if (engine->execlists.preempt_hang.count) {
> @@ -1388,7 +1387,7 @@ static int live_preempt_hang(void *arg)
>   
>   		igt_spinner_end(&spin_hi);
>   		igt_spinner_end(&spin_lo);
> -		if (igt_flush_test(i915, I915_WAIT_LOCKED)) {
> +		if (igt_flush_test(i915)) {
>   			err = -EIO;
>   			goto err_ctx_lo;
>   		}
> @@ -1785,7 +1784,7 @@ static int nop_virtual_engine(struct drm_i915_private *i915,
>   		prime, div64_u64(ktime_to_ns(times[1]), prime));
>   
>   out:
> -	if (igt_flush_test(i915, I915_WAIT_LOCKED))
> +	if (igt_flush_test(i915))
>   		err = -EIO;
>   
>   	for (nc = 0; nc < nctx; nc++) {
> @@ -1930,7 +1929,7 @@ static int mask_virtual_engine(struct drm_i915_private *i915,
>   		goto out;
>   
>   out:
> -	if (igt_flush_test(i915, I915_WAIT_LOCKED))
> +	if (igt_flush_test(i915))
>   		err = -EIO;
>   
>   	for (n = 0; n < nsibling; n++)
> @@ -2108,7 +2107,7 @@ static int bond_virtual_engine(struct drm_i915_private *i915,
>   out:
>   	for (n = 0; !IS_ERR(rq[n]); n++)
>   		i915_request_put(rq[n]);
> -	if (igt_flush_test(i915, I915_WAIT_LOCKED))
> +	if (igt_flush_test(i915))
>   		err = -EIO;
>   
>   	kernel_context_close(ctx);
> diff --git a/drivers/gpu/drm/i915/gt/selftest_timeline.c b/drivers/gpu/drm/i915/gt/selftest_timeline.c
> index 321481403165..16abfabf08c7 100644
> --- a/drivers/gpu/drm/i915/gt/selftest_timeline.c
> +++ b/drivers/gpu/drm/i915/gt/selftest_timeline.c
> @@ -6,7 +6,7 @@
>   
>   #include <linux/prime_numbers.h>
>   
> -#include "gem/i915_gem_pm.h"
> +#include "intel_engine_pm.h"
>   #include "intel_gt.h"
>   
>   #include "../selftests/i915_random.h"
> @@ -136,7 +136,6 @@ static int mock_hwsp_freelist(void *arg)
>   		goto err_put;
>   	}
>   
> -	mutex_lock(&state.i915->drm.struct_mutex);
>   	for (p = phases; p->name; p++) {
>   		pr_debug("%s(%s)\n", __func__, p->name);
>   		for_each_prime_number_from(na, 1, 2 * CACHELINES_PER_PAGE) {
> @@ -149,7 +148,6 @@ static int mock_hwsp_freelist(void *arg)
>   out:
>   	for (na = 0; na < state.max; na++)
>   		__mock_hwsp_record(&state, na, NULL);
> -	mutex_unlock(&state.i915->drm.struct_mutex);
>   	kfree(state.history);
>   err_put:
>   	drm_dev_put(&state.i915->drm);
> @@ -449,8 +447,6 @@ tl_write(struct intel_timeline *tl, struct intel_engine_cs *engine, u32 value)
>   	struct i915_request *rq;
>   	int err;
>   
> -	lockdep_assert_held(&tl->gt->i915->drm.struct_mutex); /* lazy rq refs */
> -
>   	err = intel_timeline_pin(tl);
>   	if (err) {
>   		rq = ERR_PTR(err);
> @@ -461,10 +457,14 @@ tl_write(struct intel_timeline *tl, struct intel_engine_cs *engine, u32 value)
>   	if (IS_ERR(rq))
>   		goto out_unpin;
>   
> +	i915_request_get(rq);
> +
>   	err = emit_ggtt_store_dw(rq, tl->hwsp_offset, value);
>   	i915_request_add(rq);
> -	if (err)
> +	if (err) {
> +		i915_request_put(rq);
>   		rq = ERR_PTR(err);
> +	}
>   
>   out_unpin:
>   	intel_timeline_unpin(tl);
> @@ -500,7 +500,6 @@ static int live_hwsp_engine(void *arg)
>   	struct intel_timeline **timelines;
>   	struct intel_engine_cs *engine;
>   	enum intel_engine_id id;
> -	intel_wakeref_t wakeref;
>   	unsigned long count, n;
>   	int err = 0;
>   
> @@ -515,14 +514,13 @@ static int live_hwsp_engine(void *arg)
>   	if (!timelines)
>   		return -ENOMEM;
>   
> -	mutex_lock(&i915->drm.struct_mutex);
> -	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
> -
>   	count = 0;
>   	for_each_engine(engine, i915, id) {
>   		if (!intel_engine_can_store_dword(engine))
>   			continue;
>   
> +		intel_engine_pm_get(engine);
> +
>   		for (n = 0; n < NUM_TIMELINES; n++) {
>   			struct intel_timeline *tl;
>   			struct i915_request *rq;
> @@ -530,22 +528,26 @@ static int live_hwsp_engine(void *arg)
>   			tl = checked_intel_timeline_create(i915);
>   			if (IS_ERR(tl)) {
>   				err = PTR_ERR(tl);
> -				goto out;
> +				break;
>   			}
>   
>   			rq = tl_write(tl, engine, count);
>   			if (IS_ERR(rq)) {
>   				intel_timeline_put(tl);
>   				err = PTR_ERR(rq);
> -				goto out;
> +				break;
>   			}
>   
>   			timelines[count++] = tl;
> +			i915_request_put(rq);

This was a leak until now?

>   		}
> +
> +		intel_engine_pm_put(engine);
> +		if (err)
> +			break;
>   	}
>   
> -out:
> -	if (igt_flush_test(i915, I915_WAIT_LOCKED))
> +	if (igt_flush_test(i915))
>   		err = -EIO;
>   
>   	for (n = 0; n < count; n++) {
> @@ -559,11 +561,7 @@ static int live_hwsp_engine(void *arg)
>   		intel_timeline_put(tl);
>   	}
>   
> -	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
> -	mutex_unlock(&i915->drm.struct_mutex);
> -
>   	kvfree(timelines);
> -
>   	return err;
>   #undef NUM_TIMELINES
>   }
> @@ -575,7 +573,6 @@ static int live_hwsp_alternate(void *arg)
>   	struct intel_timeline **timelines;
>   	struct intel_engine_cs *engine;
>   	enum intel_engine_id id;
> -	intel_wakeref_t wakeref;
>   	unsigned long count, n;
>   	int err = 0;
>   
> @@ -591,9 +588,6 @@ static int live_hwsp_alternate(void *arg)
>   	if (!timelines)
>   		return -ENOMEM;
>   
> -	mutex_lock(&i915->drm.struct_mutex);
> -	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
> -
>   	count = 0;
>   	for (n = 0; n < NUM_TIMELINES; n++) {
>   		for_each_engine(engine, i915, id) {
> @@ -605,11 +599,14 @@ static int live_hwsp_alternate(void *arg)
>   
>   			tl = checked_intel_timeline_create(i915);
>   			if (IS_ERR(tl)) {
> +				intel_engine_pm_put(engine);
>   				err = PTR_ERR(tl);
>   				goto out;
>   			}
>   
> +			intel_engine_pm_get(engine);
>   			rq = tl_write(tl, engine, count);
> +			intel_engine_pm_put(engine);
>   			if (IS_ERR(rq)) {
>   				intel_timeline_put(tl);
>   				err = PTR_ERR(rq);
> @@ -617,11 +614,12 @@ static int live_hwsp_alternate(void *arg)
>   			}
>   
>   			timelines[count++] = tl;
> +			i915_request_put(rq);

And this.

>   		}
>   	}
>   
>   out:
> -	if (igt_flush_test(i915, I915_WAIT_LOCKED))
> +	if (igt_flush_test(i915))
>   		err = -EIO;
>   
>   	for (n = 0; n < count; n++) {
> @@ -635,11 +633,7 @@ static int live_hwsp_alternate(void *arg)
>   		intel_timeline_put(tl);
>   	}
>   
> -	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
> -	mutex_unlock(&i915->drm.struct_mutex);
> -
>   	kvfree(timelines);
> -
>   	return err;
>   #undef NUM_TIMELINES
>   }
> @@ -650,7 +644,6 @@ static int live_hwsp_wrap(void *arg)
>   	struct intel_engine_cs *engine;
>   	struct intel_timeline *tl;
>   	enum intel_engine_id id;
> -	intel_wakeref_t wakeref;
>   	int err = 0;
>   
>   	/*
> @@ -658,14 +651,10 @@ static int live_hwsp_wrap(void *arg)
>   	 * foreign GPU references.
>   	 */
>   
> -	mutex_lock(&i915->drm.struct_mutex);
> -	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
> -
>   	tl = intel_timeline_create(&i915->gt, NULL);
> -	if (IS_ERR(tl)) {
> -		err = PTR_ERR(tl);
> -		goto out_rpm;
> -	}
> +	if (IS_ERR(tl))
> +		return PTR_ERR(tl);
> +
>   	if (!tl->has_initial_breadcrumb || !tl->hwsp_cacheline)
>   		goto out_free;
>   
> @@ -681,7 +670,9 @@ static int live_hwsp_wrap(void *arg)
>   		if (!intel_engine_can_store_dword(engine))
>   			continue;
>   
> +		intel_engine_pm_get(engine);
>   		rq = i915_request_create(engine->kernel_context);
> +		intel_engine_pm_put(engine);
>   		if (IS_ERR(rq)) {
>   			err = PTR_ERR(rq);
>   			goto out;
> @@ -747,16 +738,12 @@ static int live_hwsp_wrap(void *arg)
>   	}
>   

No i915_request_put in this one and I can't see why it would be different.

>   out:
> -	if (igt_flush_test(i915, I915_WAIT_LOCKED))
> +	if (igt_flush_test(i915))
>   		err = -EIO;
>   
>   	intel_timeline_unpin(tl);
>   out_free:
>   	intel_timeline_put(tl);
> -out_rpm:
> -	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
> -	mutex_unlock(&i915->drm.struct_mutex);
> -
>   	return err;
>   }
>   
> @@ -765,7 +752,6 @@ static int live_hwsp_recycle(void *arg)
>   	struct drm_i915_private *i915 = arg;
>   	struct intel_engine_cs *engine;
>   	enum intel_engine_id id;
> -	intel_wakeref_t wakeref;
>   	unsigned long count;
>   	int err = 0;
>   
> @@ -775,9 +761,6 @@ static int live_hwsp_recycle(void *arg)
>   	 * want to confuse ourselves or the GPU.
>   	 */
>   
> -	mutex_lock(&i915->drm.struct_mutex);
> -	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
> -
>   	count = 0;
>   	for_each_engine(engine, i915, id) {
>   		IGT_TIMEOUT(end_time);
> @@ -785,6 +768,8 @@ static int live_hwsp_recycle(void *arg)
>   		if (!intel_engine_can_store_dword(engine))
>   			continue;
>   
> +		intel_engine_pm_get(engine);
> +
>   		do {
>   			struct intel_timeline *tl;
>   			struct i915_request *rq;
> @@ -792,21 +777,22 @@ static int live_hwsp_recycle(void *arg)
>   			tl = checked_intel_timeline_create(i915);
>   			if (IS_ERR(tl)) {
>   				err = PTR_ERR(tl);
> -				goto out;
> +				break;
>   			}
>   
>   			rq = tl_write(tl, engine, count);
>   			if (IS_ERR(rq)) {
>   				intel_timeline_put(tl);
>   				err = PTR_ERR(rq);
> -				goto out;
> +				break;
>   			}
>   
>   			if (i915_request_wait(rq, 0, HZ / 5) < 0) {
>   				pr_err("Wait for timeline writes timed out!\n");
> +				i915_request_put(rq);
>   				intel_timeline_put(tl);
>   				err = -EIO;
> -				goto out;
> +				break;
>   			}
>   
>   			if (*tl->hwsp_seqno != count) {
> @@ -815,17 +801,18 @@ static int live_hwsp_recycle(void *arg)
>   				err = -EINVAL;
>   			}
>   
> +			i915_request_put(rq);
>   			intel_timeline_put(tl);
>   			count++;
>   
>   			if (err)
> -				goto out;
> +				break;
>   		} while (!__igt_timeout(end_time, NULL));
> -	}
>   
> -out:
> -	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
> -	mutex_unlock(&i915->drm.struct_mutex);
> +		intel_engine_pm_put(engine);
> +		if (err)
> +			break;
> +	}
>   
>   	return err;
>   }
> diff --git a/drivers/gpu/drm/i915/gt/selftest_workarounds.c b/drivers/gpu/drm/i915/gt/selftest_workarounds.c
> index 999a98f00494..06351fefbbf3 100644
> --- a/drivers/gpu/drm/i915/gt/selftest_workarounds.c
> +++ b/drivers/gpu/drm/i915/gt/selftest_workarounds.c
> @@ -676,7 +676,7 @@ static int check_dirty_whitelist(struct i915_gem_context *ctx,
>   			break;
>   	}
>   
> -	if (igt_flush_test(ctx->i915, I915_WAIT_LOCKED))
> +	if (igt_flush_test(ctx->i915))
>   		err = -EIO;
>   out_batch:
>   	i915_vma_unpin_and_release(&batch, 0);
> @@ -1090,7 +1090,7 @@ static int live_isolated_whitelist(void *arg)
>   		kernel_context_close(client[i].ctx);
>   	}
>   
> -	if (igt_flush_test(i915, I915_WAIT_LOCKED))
> +	if (igt_flush_test(i915))
>   		err = -EIO;
>   
>   	return err;
> @@ -1248,7 +1248,7 @@ live_engine_reset_workarounds(void *arg)
>   	igt_global_reset_unlock(&i915->gt);
>   	kernel_context_close(ctx);
>   
> -	igt_flush_test(i915, I915_WAIT_LOCKED);
> +	igt_flush_test(i915);
>   
>   	return ret;
>   }
> diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
> index 30b0b592e20d..55f0fc03aa3e 100644
> --- a/drivers/gpu/drm/i915/i915_debugfs.c
> +++ b/drivers/gpu/drm/i915/i915_debugfs.c
> @@ -3601,6 +3601,7 @@ static int
>   i915_drop_caches_set(void *data, u64 val)
>   {
>   	struct drm_i915_private *i915 = data;
> +	int ret;
>   
>   	DRM_DEBUG("Dropping caches: 0x%08llx [0x%08llx]\n",
>   		  val, val & DROP_ALL);
> @@ -3610,40 +3611,21 @@ i915_drop_caches_set(void *data, u64 val)
>   		     I915_IDLE_ENGINES_TIMEOUT))
>   		intel_gt_set_wedged(&i915->gt);
>   
> -	/* No need to check and wait for gpu resets, only libdrm auto-restarts
> -	 * on ioctls on -EAGAIN. */
> -	if (val & (DROP_ACTIVE | DROP_IDLE | DROP_RETIRE | DROP_RESET_SEQNO)) {
> -		int ret;
> +	if (val & DROP_RETIRE)
> +		i915_retire_requests(i915);
>   
> -		ret = mutex_lock_interruptible(&i915->drm.struct_mutex);
> +	if (val & (DROP_IDLE | DROP_ACTIVE)) {
> +		ret = i915_gem_wait_for_idle(i915,
> +					     I915_WAIT_INTERRUPTIBLE,
> +					     MAX_SCHEDULE_TIMEOUT);
>   		if (ret)
>   			return ret;
> +	}
>   
> -		/*
> -		 * To finish the flush of the idle_worker, we must complete
> -		 * the switch-to-kernel-context, which requires a double
> -		 * pass through wait_for_idle: first queues the switch,
> -		 * second waits for the switch.
> -		 */
> -		if (ret == 0 && val & (DROP_IDLE | DROP_ACTIVE))
> -			ret = i915_gem_wait_for_idle(i915,
> -						     I915_WAIT_INTERRUPTIBLE |
> -						     I915_WAIT_LOCKED,
> -						     MAX_SCHEDULE_TIMEOUT);
> -
> -		if (ret == 0 && val & DROP_IDLE)
> -			ret = i915_gem_wait_for_idle(i915,
> -						     I915_WAIT_INTERRUPTIBLE |
> -						     I915_WAIT_LOCKED,
> -						     MAX_SCHEDULE_TIMEOUT);
> -
> -		if (val & DROP_RETIRE)
> -			i915_retire_requests(i915);
> -
> -		mutex_unlock(&i915->drm.struct_mutex);
> -
> -		if (ret == 0 && val & DROP_IDLE)
> -			ret = intel_gt_pm_wait_for_idle(&i915->gt);
> +	if (val & DROP_IDLE) {
> +		ret = intel_gt_pm_wait_for_idle(&i915->gt);
> +		if (ret)
> +			return ret;
>   	}
>   
>   	if (val & DROP_RESET_ACTIVE && intel_gt_terminally_wedged(&i915->gt))
> diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
> index 1aadab1cdd24..225fd22af858 100644
> --- a/drivers/gpu/drm/i915/i915_gem.c
> +++ b/drivers/gpu/drm/i915/i915_gem.c
> @@ -951,19 +951,16 @@ int i915_gem_wait_for_idle(struct drm_i915_private *i915,
>   	if (!intel_gt_pm_is_awake(&i915->gt))
>   		return 0;
>   
> -	GEM_TRACE("flags=%x (%s), timeout=%ld%s\n",
> -		  flags, flags & I915_WAIT_LOCKED ? "locked" : "unlocked",
> -		  timeout, timeout == MAX_SCHEDULE_TIMEOUT ? " (forever)" : "");
> -
> -	timeout = wait_for_timelines(i915, flags, timeout);
> -	if (timeout < 0)
> -		return timeout;
> +	do {
> +		timeout = wait_for_timelines(i915, flags, timeout);
> +		if (timeout < 0)
> +			return timeout;
>   
> -	if (flags & I915_WAIT_LOCKED) {
> -		lockdep_assert_held(&i915->drm.struct_mutex);
> +		cond_resched();
> +		if (signal_pending(current))
> +			return -EINTR;
>   
> -		i915_retire_requests(i915);
> -	}
> +	} while (i915_retire_requests(i915));
>   
>   	return 0;
>   }
> diff --git a/drivers/gpu/drm/i915/i915_request.h b/drivers/gpu/drm/i915/i915_request.h
> index 3251d2bdbeea..57a2193c64d1 100644
> --- a/drivers/gpu/drm/i915/i915_request.h
> +++ b/drivers/gpu/drm/i915/i915_request.h
> @@ -308,10 +308,9 @@ long i915_request_wait(struct i915_request *rq,
>   		       long timeout)
>   	__attribute__((nonnull(1)));
>   #define I915_WAIT_INTERRUPTIBLE	BIT(0)
> -#define I915_WAIT_LOCKED	BIT(1) /* struct_mutex held, handle GPU reset */
> -#define I915_WAIT_PRIORITY	BIT(2) /* small priority bump for the request */
> -#define I915_WAIT_ALL		BIT(3) /* used by i915_gem_object_wait() */
> -#define I915_WAIT_FOR_IDLE_BOOST BIT(4)
> +#define I915_WAIT_PRIORITY	BIT(1) /* small priority bump for the request */
> +#define I915_WAIT_ALL		BIT(2) /* used by i915_gem_object_wait() */
> +#define I915_WAIT_FOR_IDLE_BOOST BIT(3)
>   
>   static inline bool i915_request_signaled(const struct i915_request *rq)
>   {
> diff --git a/drivers/gpu/drm/i915/selftests/i915_active.c b/drivers/gpu/drm/i915/selftests/i915_active.c
> index af5827aac7b2..ff1337e34522 100644
> --- a/drivers/gpu/drm/i915/selftests/i915_active.c
> +++ b/drivers/gpu/drm/i915/selftests/i915_active.c
> @@ -164,10 +164,8 @@ static int live_active_wait(void *arg)
>   
>   	__live_put(active);
>   
> -	mutex_lock(&i915->drm.struct_mutex);
> -	if (igt_flush_test(i915, I915_WAIT_LOCKED))
> +	if (igt_flush_test(i915))
>   		err = -EIO;
> -	mutex_unlock(&i915->drm.struct_mutex);
>   
>   	return err;
>   }
> @@ -185,10 +183,8 @@ static int live_active_retire(void *arg)
>   		return PTR_ERR(active);
>   
>   	/* waits for & retires all requests */
> -	mutex_lock(&i915->drm.struct_mutex);
> -	if (igt_flush_test(i915, I915_WAIT_LOCKED))
> +	if (igt_flush_test(i915))
>   		err = -EIO;
> -	mutex_unlock(&i915->drm.struct_mutex);
>   
>   	if (!READ_ONCE(active->retired)) {
>   		pr_err("i915_active not retired after flushing!\n");
> diff --git a/drivers/gpu/drm/i915/selftests/i915_gem_evict.c b/drivers/gpu/drm/i915/selftests/i915_gem_evict.c
> index ba6064147173..42139db0d69c 100644
> --- a/drivers/gpu/drm/i915/selftests/i915_gem_evict.c
> +++ b/drivers/gpu/drm/i915/selftests/i915_gem_evict.c
> @@ -521,7 +521,7 @@ static int igt_evict_contexts(void *arg)
>   
>   	mutex_lock(&i915->ggtt.vm.mutex);
>   out_locked:
> -	if (igt_flush_test(i915, I915_WAIT_LOCKED))
> +	if (igt_flush_test(i915))
>   		err = -EIO;
>   	while (reserved) {
>   		struct reserved *next = reserved->next;
> diff --git a/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c b/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
> index 4235fa401956..729a53eb6244 100644
> --- a/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
> +++ b/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
> @@ -1709,12 +1709,8 @@ int i915_gem_gtt_mock_selftests(void)
>   
>   	err = i915_subtests(tests, ggtt);
>   
> -	mutex_lock(&i915->drm.struct_mutex);
>   	mock_device_flush(i915);
> -	mutex_unlock(&i915->drm.struct_mutex);
> -
>   	i915_gem_drain_freed_objects(i915);
> -
>   	mock_fini_ggtt(ggtt);
>   	kfree(ggtt);
>   out_put:
> diff --git a/drivers/gpu/drm/i915/selftests/i915_request.c b/drivers/gpu/drm/i915/selftests/i915_request.c
> index b3688543ed7d..d046395845da 100644
> --- a/drivers/gpu/drm/i915/selftests/i915_request.c
> +++ b/drivers/gpu/drm/i915/selftests/i915_request.c
> @@ -41,21 +41,16 @@ static int igt_add_request(void *arg)
>   {
>   	struct drm_i915_private *i915 = arg;
>   	struct i915_request *request;
> -	int err = -ENOMEM;
>   
>   	/* Basic preliminary test to create a request and let it loose! */
>   
> -	mutex_lock(&i915->drm.struct_mutex);
>   	request = mock_request(i915->engine[RCS0]->kernel_context, HZ / 10);
>   	if (!request)
> -		goto out_unlock;
> +		return -ENOMEM;
>   
>   	i915_request_add(request);
>   
> -	err = 0;
> -out_unlock:
> -	mutex_unlock(&i915->drm.struct_mutex);
> -	return err;
> +	return 0;
>   }
>   
>   static int igt_wait_request(void *arg)
> @@ -67,12 +62,10 @@ static int igt_wait_request(void *arg)
>   
>   	/* Submit a request, then wait upon it */
>   
> -	mutex_lock(&i915->drm.struct_mutex);
>   	request = mock_request(i915->engine[RCS0]->kernel_context, T);
> -	if (!request) {
> -		err = -ENOMEM;
> -		goto out_unlock;
> -	}
> +	if (!request)
> +		return -ENOMEM;
> +
>   	i915_request_get(request);
>   
>   	if (i915_request_wait(request, 0, 0) != -ETIME) {
> @@ -125,9 +118,7 @@ static int igt_wait_request(void *arg)
>   	err = 0;
>   out_request:
>   	i915_request_put(request);
> -out_unlock:
>   	mock_device_flush(i915);
> -	mutex_unlock(&i915->drm.struct_mutex);
>   	return err;
>   }
>   
> @@ -140,52 +131,45 @@ static int igt_fence_wait(void *arg)
>   
>   	/* Submit a request, treat it as a fence and wait upon it */
>   
> -	mutex_lock(&i915->drm.struct_mutex);
>   	request = mock_request(i915->engine[RCS0]->kernel_context, T);
> -	if (!request) {
> -		err = -ENOMEM;
> -		goto out_locked;
> -	}
> +	if (!request)
> +		return -ENOMEM;
>   
>   	if (dma_fence_wait_timeout(&request->fence, false, T) != -ETIME) {
>   		pr_err("fence wait success before submit (expected timeout)!\n");
> -		goto out_locked;
> +		goto out;
>   	}
>   
>   	i915_request_add(request);
> -	mutex_unlock(&i915->drm.struct_mutex);
>   
>   	if (dma_fence_is_signaled(&request->fence)) {
>   		pr_err("fence signaled immediately!\n");
> -		goto out_device;
> +		goto out;
>   	}
>   
>   	if (dma_fence_wait_timeout(&request->fence, false, T / 2) != -ETIME) {
>   		pr_err("fence wait success after submit (expected timeout)!\n");
> -		goto out_device;
> +		goto out;
>   	}
>   
>   	if (dma_fence_wait_timeout(&request->fence, false, T) <= 0) {
>   		pr_err("fence wait timed out (expected success)!\n");
> -		goto out_device;
> +		goto out;
>   	}
>   
>   	if (!dma_fence_is_signaled(&request->fence)) {
>   		pr_err("fence unsignaled after waiting!\n");
> -		goto out_device;
> +		goto out;
>   	}
>   
>   	if (dma_fence_wait_timeout(&request->fence, false, T) <= 0) {
>   		pr_err("fence wait timed out when complete (expected success)!\n");
> -		goto out_device;
> +		goto out;
>   	}
>   
>   	err = 0;
> -out_device:
> -	mutex_lock(&i915->drm.struct_mutex);
> -out_locked:
> +out:
>   	mock_device_flush(i915);
> -	mutex_unlock(&i915->drm.struct_mutex);
>   	return err;
>   }
>   
> @@ -199,6 +183,8 @@ static int igt_request_rewind(void *arg)
>   
>   	mutex_lock(&i915->drm.struct_mutex);
>   	ctx[0] = mock_context(i915, "A");
> +	mutex_unlock(&i915->drm.struct_mutex);
> +
>   	ce = i915_gem_context_get_engine(ctx[0], RCS0);
>   	GEM_BUG_ON(IS_ERR(ce));
>   	request = mock_request(ce, 2 * HZ);
> @@ -211,7 +197,10 @@ static int igt_request_rewind(void *arg)
>   	i915_request_get(request);
>   	i915_request_add(request);
>   
> +	mutex_lock(&i915->drm.struct_mutex);
>   	ctx[1] = mock_context(i915, "B");
> +	mutex_unlock(&i915->drm.struct_mutex);
> +
>   	ce = i915_gem_context_get_engine(ctx[1], RCS0);
>   	GEM_BUG_ON(IS_ERR(ce));
>   	vip = mock_request(ce, 0);
> @@ -233,7 +222,6 @@ static int igt_request_rewind(void *arg)
>   	request->engine->submit_request(request);
>   	rcu_read_unlock();
>   
> -	mutex_unlock(&i915->drm.struct_mutex);
>   
>   	if (i915_request_wait(vip, 0, HZ) == -ETIME) {
>   		pr_err("timed out waiting for high priority request\n");
> @@ -248,14 +236,12 @@ static int igt_request_rewind(void *arg)
>   	err = 0;
>   err:
>   	i915_request_put(vip);
> -	mutex_lock(&i915->drm.struct_mutex);
>   err_context_1:
>   	mock_context_close(ctx[1]);
>   	i915_request_put(request);
>   err_context_0:
>   	mock_context_close(ctx[0]);
>   	mock_device_flush(i915);
> -	mutex_unlock(&i915->drm.struct_mutex);
>   	return err;
>   }
>   
> @@ -282,7 +268,6 @@ __live_request_alloc(struct intel_context *ce)
>   static int __igt_breadcrumbs_smoketest(void *arg)
>   {
>   	struct smoketest *t = arg;
> -	struct mutex * const BKL = &t->engine->i915->drm.struct_mutex;
>   	const unsigned int max_batch = min(t->ncontexts, t->max_batch) - 1;
>   	const unsigned int total = 4 * t->ncontexts + 1;
>   	unsigned int num_waits = 0, num_fences = 0;
> @@ -337,14 +322,11 @@ static int __igt_breadcrumbs_smoketest(void *arg)
>   			struct i915_request *rq;
>   			struct intel_context *ce;
>   
> -			mutex_lock(BKL);
> -
>   			ce = i915_gem_context_get_engine(ctx, t->engine->legacy_idx);
>   			GEM_BUG_ON(IS_ERR(ce));
>   			rq = t->request_alloc(ce);
>   			intel_context_put(ce);
>   			if (IS_ERR(rq)) {
> -				mutex_unlock(BKL);
>   				err = PTR_ERR(rq);
>   				count = n;
>   				break;
> @@ -357,8 +339,6 @@ static int __igt_breadcrumbs_smoketest(void *arg)
>   			requests[n] = i915_request_get(rq);
>   			i915_request_add(rq);
>   
> -			mutex_unlock(BKL);
> -
>   			if (err >= 0)
>   				err = i915_sw_fence_await_dma_fence(wait,
>   								    &rq->fence,
> @@ -457,15 +437,15 @@ static int mock_breadcrumbs_smoketest(void *arg)
>   		goto out_threads;
>   	}
>   
> -	mutex_lock(&t.engine->i915->drm.struct_mutex);
>   	for (n = 0; n < t.ncontexts; n++) {
> +		mutex_lock(&t.engine->i915->drm.struct_mutex);
>   		t.contexts[n] = mock_context(t.engine->i915, "mock");
> +		mutex_unlock(&t.engine->i915->drm.struct_mutex);
>   		if (!t.contexts[n]) {
>   			ret = -ENOMEM;
>   			goto out_contexts;
>   		}
>   	}
> -	mutex_unlock(&t.engine->i915->drm.struct_mutex);
>   
>   	for (n = 0; n < ncpus; n++) {
>   		threads[n] = kthread_run(__igt_breadcrumbs_smoketest,
> @@ -495,18 +475,15 @@ static int mock_breadcrumbs_smoketest(void *arg)
>   		atomic_long_read(&t.num_fences),
>   		ncpus);
>   
> -	mutex_lock(&t.engine->i915->drm.struct_mutex);
>   out_contexts:
>   	for (n = 0; n < t.ncontexts; n++) {
>   		if (!t.contexts[n])
>   			break;
>   		mock_context_close(t.contexts[n]);
>   	}
> -	mutex_unlock(&t.engine->i915->drm.struct_mutex);
>   	kfree(t.contexts);
>   out_threads:
>   	kfree(threads);
> -
>   	return ret;
>   }
>   
> @@ -539,7 +516,6 @@ static int live_nop_request(void *arg)
>   {
>   	struct drm_i915_private *i915 = arg;
>   	struct intel_engine_cs *engine;
> -	intel_wakeref_t wakeref;
>   	struct igt_live_test t;
>   	unsigned int id;
>   	int err = -ENODEV;
> @@ -549,9 +525,6 @@ static int live_nop_request(void *arg)
>   	 * the overhead of submitting requests to the hardware.
>   	 */
>   
> -	mutex_lock(&i915->drm.struct_mutex);
> -	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
> -
>   	for_each_engine(engine, i915, id) {
>   		struct i915_request *request = NULL;
>   		unsigned long n, prime;
> @@ -560,17 +533,15 @@ static int live_nop_request(void *arg)
>   
>   		err = igt_live_test_begin(&t, i915, __func__, engine->name);
>   		if (err)
> -			goto out_unlock;
> +			return err;
>   
>   		for_each_prime_number_from(prime, 1, 8192) {
>   			times[1] = ktime_get_raw();
>   
>   			for (n = 0; n < prime; n++) {
>   				request = i915_request_create(engine->kernel_context);
> -				if (IS_ERR(request)) {
> -					err = PTR_ERR(request);
> -					goto out_unlock;
> -				}
> +				if (IS_ERR(request))
> +					return PTR_ERR(request);
>   
>   				/* This space is left intentionally blank.
>   				 *
> @@ -599,7 +570,7 @@ static int live_nop_request(void *arg)
>   
>   		err = igt_live_test_end(&t);
>   		if (err)
> -			goto out_unlock;
> +			return err;
>   
>   		pr_info("Request latencies on %s: 1 = %lluns, %lu = %lluns\n",
>   			engine->name,
> @@ -607,9 +578,6 @@ static int live_nop_request(void *arg)
>   			prime, div64_u64(ktime_to_ns(times[1]), prime));
>   	}
>   
> -out_unlock:
> -	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
> -	mutex_unlock(&i915->drm.struct_mutex);
>   	return err;
>   }
>   
> @@ -681,7 +649,6 @@ static int live_empty_request(void *arg)
>   {
>   	struct drm_i915_private *i915 = arg;
>   	struct intel_engine_cs *engine;
> -	intel_wakeref_t wakeref;
>   	struct igt_live_test t;
>   	struct i915_vma *batch;
>   	unsigned int id;
> @@ -692,14 +659,9 @@ static int live_empty_request(void *arg)
>   	 * the overhead of submitting requests to the hardware.
>   	 */
>   
> -	mutex_lock(&i915->drm.struct_mutex);
> -	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
> -
>   	batch = empty_batch(i915);
> -	if (IS_ERR(batch)) {
> -		err = PTR_ERR(batch);
> -		goto out_unlock;
> -	}
> +	if (IS_ERR(batch))
> +		return PTR_ERR(batch);
>   
>   	for_each_engine(engine, i915, id) {
>   		IGT_TIMEOUT(end_time);
> @@ -752,9 +714,6 @@ static int live_empty_request(void *arg)
>   out_batch:
>   	i915_vma_unpin(batch);
>   	i915_vma_put(batch);
> -out_unlock:
> -	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
> -	mutex_unlock(&i915->drm.struct_mutex);
>   	return err;
>   }
>   
> @@ -834,7 +793,6 @@ static int live_all_engines(void *arg)
>   	struct drm_i915_private *i915 = arg;
>   	struct intel_engine_cs *engine;
>   	struct i915_request *request[I915_NUM_ENGINES];
> -	intel_wakeref_t wakeref;
>   	struct igt_live_test t;
>   	struct i915_vma *batch;
>   	unsigned int id;
> @@ -845,18 +803,15 @@ static int live_all_engines(void *arg)
>   	 * block doing so, and that they don't complete too soon.
>   	 */
>   
> -	mutex_lock(&i915->drm.struct_mutex);
> -	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
> -
>   	err = igt_live_test_begin(&t, i915, __func__, "");
>   	if (err)
> -		goto out_unlock;
> +		return err;
>   
>   	batch = recursive_batch(i915);
>   	if (IS_ERR(batch)) {
>   		err = PTR_ERR(batch);
>   		pr_err("%s: Unable to create batch, err=%d\n", __func__, err);
> -		goto out_unlock;
> +		return err;
>   	}
>   
>   	for_each_engine(engine, i915, id) {
> @@ -926,9 +881,6 @@ static int live_all_engines(void *arg)
>   			i915_request_put(request[id]);
>   	i915_vma_unpin(batch);
>   	i915_vma_put(batch);
> -out_unlock:
> -	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
> -	mutex_unlock(&i915->drm.struct_mutex);
>   	return err;
>   }
>   
> @@ -938,7 +890,6 @@ static int live_sequential_engines(void *arg)
>   	struct i915_request *request[I915_NUM_ENGINES] = {};
>   	struct i915_request *prev = NULL;
>   	struct intel_engine_cs *engine;
> -	intel_wakeref_t wakeref;
>   	struct igt_live_test t;
>   	unsigned int id;
>   	int err;
> @@ -949,12 +900,9 @@ static int live_sequential_engines(void *arg)
>   	 * they are running on independent engines.
>   	 */
>   
> -	mutex_lock(&i915->drm.struct_mutex);
> -	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
> -
>   	err = igt_live_test_begin(&t, i915, __func__, "");
>   	if (err)
> -		goto out_unlock;
> +		return err;
>   
>   	for_each_engine(engine, i915, id) {
>   		struct i915_vma *batch;
> @@ -964,7 +912,7 @@ static int live_sequential_engines(void *arg)
>   			err = PTR_ERR(batch);
>   			pr_err("%s: Unable to create batch for %s, err=%d\n",
>   			       __func__, engine->name, err);
> -			goto out_unlock;
> +			return err;
>   		}
>   
>   		request[id] = i915_request_create(engine->kernel_context);
> @@ -1056,9 +1004,6 @@ static int live_sequential_engines(void *arg)
>   		i915_vma_put(request[id]->batch);
>   		i915_request_put(request[id]);
>   	}
> -out_unlock:
> -	intel_runtime_pm_put(&i915->runtime_pm, wakeref);
> -	mutex_unlock(&i915->drm.struct_mutex);
>   	return err;
>   }
>   
> @@ -1149,9 +1094,10 @@ static int live_breadcrumbs_smoketest(void *arg)
>   		goto out_threads;
>   	}
>   
> -	mutex_lock(&i915->drm.struct_mutex);
>   	for (n = 0; n < t[0].ncontexts; n++) {
> +		mutex_lock(&i915->drm.struct_mutex);
>   		t[0].contexts[n] = live_context(i915, file);
> +		mutex_unlock(&i915->drm.struct_mutex);
>   		if (!t[0].contexts[n]) {
>   			ret = -ENOMEM;
>   			goto out_contexts;
> @@ -1168,7 +1114,6 @@ static int live_breadcrumbs_smoketest(void *arg)
>   		t[id].max_batch = max_batches(t[0].contexts[0], engine);
>   		if (t[id].max_batch < 0) {
>   			ret = t[id].max_batch;
> -			mutex_unlock(&i915->drm.struct_mutex);
>   			goto out_flush;
>   		}
>   		/* One ring interleaved between requests from all cpus */
> @@ -1183,7 +1128,6 @@ static int live_breadcrumbs_smoketest(void *arg)
>   					  &t[id], "igt/%d.%d", id, n);
>   			if (IS_ERR(tsk)) {
>   				ret = PTR_ERR(tsk);
> -				mutex_unlock(&i915->drm.struct_mutex);
>   				goto out_flush;
>   			}
>   
> @@ -1191,7 +1135,6 @@ static int live_breadcrumbs_smoketest(void *arg)
>   			threads[id * ncpus + n] = tsk;
>   		}
>   	}
> -	mutex_unlock(&i915->drm.struct_mutex);
>   
>   	msleep(jiffies_to_msecs(i915_selftest.timeout_jiffies));
>   
> @@ -1219,10 +1162,8 @@ static int live_breadcrumbs_smoketest(void *arg)
>   	pr_info("Completed %lu waits for %lu fences across %d engines and %d cpus\n",
>   		num_waits, num_fences, RUNTIME_INFO(i915)->num_engines, ncpus);
>   
> -	mutex_lock(&i915->drm.struct_mutex);
>   	ret = igt_live_test_end(&live) ?: ret;
>   out_contexts:
> -	mutex_unlock(&i915->drm.struct_mutex);
>   	kfree(t[0].contexts);
>   out_threads:
>   	kfree(threads);
> diff --git a/drivers/gpu/drm/i915/selftests/i915_selftest.c b/drivers/gpu/drm/i915/selftests/i915_selftest.c
> index 438ea0eaa416..825a8286cbe8 100644
> --- a/drivers/gpu/drm/i915/selftests/i915_selftest.c
> +++ b/drivers/gpu/drm/i915/selftests/i915_selftest.c
> @@ -263,10 +263,8 @@ int __i915_live_teardown(int err, void *data)
>   {
>   	struct drm_i915_private *i915 = data;
>   
> -	mutex_lock(&i915->drm.struct_mutex);
> -	if (igt_flush_test(i915, I915_WAIT_LOCKED))
> +	if (igt_flush_test(i915))
>   		err = -EIO;
> -	mutex_unlock(&i915->drm.struct_mutex);
>   
>   	i915_gem_drain_freed_objects(i915);
>   
> @@ -284,10 +282,8 @@ int __intel_gt_live_teardown(int err, void *data)
>   {
>   	struct intel_gt *gt = data;
>   
> -	mutex_lock(&gt->i915->drm.struct_mutex);
> -	if (igt_flush_test(gt->i915, I915_WAIT_LOCKED))
> +	if (igt_flush_test(gt->i915))
>   		err = -EIO;
> -	mutex_unlock(&gt->i915->drm.struct_mutex);
>   
>   	i915_gem_drain_freed_objects(gt->i915);
>   
> diff --git a/drivers/gpu/drm/i915/selftests/i915_vma.c b/drivers/gpu/drm/i915/selftests/i915_vma.c
> index e0ca12c17a7f..6c1705058b93 100644
> --- a/drivers/gpu/drm/i915/selftests/i915_vma.c
> +++ b/drivers/gpu/drm/i915/selftests/i915_vma.c
> @@ -835,12 +835,8 @@ int i915_vma_mock_selftests(void)
>   
>   	err = i915_subtests(tests, ggtt);
>   
> -	mutex_lock(&i915->drm.struct_mutex);
>   	mock_device_flush(i915);
> -	mutex_unlock(&i915->drm.struct_mutex);
> -
>   	i915_gem_drain_freed_objects(i915);
> -
>   	mock_fini_ggtt(ggtt);
>   	kfree(ggtt);
>   out_put:
> diff --git a/drivers/gpu/drm/i915/selftests/igt_flush_test.c b/drivers/gpu/drm/i915/selftests/igt_flush_test.c
> index d3b5eb402d33..2a5fbe46ea9f 100644
> --- a/drivers/gpu/drm/i915/selftests/igt_flush_test.c
> +++ b/drivers/gpu/drm/i915/selftests/igt_flush_test.c
> @@ -12,31 +12,25 @@
>   
>   #include "igt_flush_test.h"
>   
> -int igt_flush_test(struct drm_i915_private *i915, unsigned int flags)
> +int igt_flush_test(struct drm_i915_private *i915)
>   {
>   	int ret = intel_gt_is_wedged(&i915->gt) ? -EIO : 0;
> -	int repeat = !!(flags & I915_WAIT_LOCKED);
>   
>   	cond_resched();
>   
> -	do {
> -		if (i915_gem_wait_for_idle(i915, flags, HZ / 5) == -ETIME) {
> -			pr_err("%pS timed out, cancelling all further testing.\n",
> -			       __builtin_return_address(0));
> +	i915_retire_requests(i915);

Do you need this one or it would be better without, for clarity, given 
there is one at the end? i915_gem_wait_for_idle will retire all it can.

> +	if (i915_gem_wait_for_idle(i915, 0, HZ / 5) == -ETIME) {
> +		pr_err("%pS timed out, cancelling all further testing.\n",
> +		       __builtin_return_address(0));
>   
> -			GEM_TRACE("%pS timed out.\n",
> -				  __builtin_return_address(0));
> -			GEM_TRACE_DUMP();
> +		GEM_TRACE("%pS timed out.\n",
> +			  __builtin_return_address(0));
> +		GEM_TRACE_DUMP();
>   
> -			intel_gt_set_wedged(&i915->gt);
> -			repeat = 0;
> -			ret = -EIO;
> -		}
> -
> -		/* Ensure we also flush after wedging. */
> -		if (flags & I915_WAIT_LOCKED)
> -			i915_retire_requests(i915);
> -	} while (repeat--);
> +		intel_gt_set_wedged(&i915->gt);
> +		ret = -EIO;
> +	}
> +	i915_retire_requests(i915);
>   
>   	return ret;
>   }
> diff --git a/drivers/gpu/drm/i915/selftests/igt_flush_test.h b/drivers/gpu/drm/i915/selftests/igt_flush_test.h
> index 63e009927c43..7541fa74e641 100644
> --- a/drivers/gpu/drm/i915/selftests/igt_flush_test.h
> +++ b/drivers/gpu/drm/i915/selftests/igt_flush_test.h
> @@ -9,6 +9,6 @@
>   
>   struct drm_i915_private;
>   
> -int igt_flush_test(struct drm_i915_private *i915, unsigned int flags);
> +int igt_flush_test(struct drm_i915_private *i915);
>   
>   #endif /* IGT_FLUSH_TEST_H */
> diff --git a/drivers/gpu/drm/i915/selftests/igt_live_test.c b/drivers/gpu/drm/i915/selftests/igt_live_test.c
> index 3e902761cd16..04a6f88fdf64 100644
> --- a/drivers/gpu/drm/i915/selftests/igt_live_test.c
> +++ b/drivers/gpu/drm/i915/selftests/igt_live_test.c
> @@ -19,15 +19,12 @@ int igt_live_test_begin(struct igt_live_test *t,
>   	enum intel_engine_id id;
>   	int err;
>   
> -	lockdep_assert_held(&i915->drm.struct_mutex);
> -
>   	t->i915 = i915;
>   	t->func = func;
>   	t->name = name;
>   
>   	err = i915_gem_wait_for_idle(i915,
> -				     I915_WAIT_INTERRUPTIBLE |
> -				     I915_WAIT_LOCKED,
> +				     I915_WAIT_INTERRUPTIBLE,
>   				     MAX_SCHEDULE_TIMEOUT);
>   	if (err) {
>   		pr_err("%s(%s): failed to idle before, with err=%d!",
> @@ -50,9 +47,7 @@ int igt_live_test_end(struct igt_live_test *t)
>   	struct intel_engine_cs *engine;
>   	enum intel_engine_id id;
>   
> -	lockdep_assert_held(&i915->drm.struct_mutex);
> -
> -	if (igt_flush_test(i915, I915_WAIT_LOCKED))
> +	if (igt_flush_test(i915))
>   		return -EIO;
>   
>   	if (t->reset_global != i915_reset_count(&i915->gpu_error)) {
> diff --git a/drivers/gpu/drm/i915/selftests/mock_gem_device.c b/drivers/gpu/drm/i915/selftests/mock_gem_device.c
> index 01a89c071bf5..1956006a0d5b 100644
> --- a/drivers/gpu/drm/i915/selftests/mock_gem_device.c
> +++ b/drivers/gpu/drm/i915/selftests/mock_gem_device.c
> @@ -41,8 +41,6 @@ void mock_device_flush(struct drm_i915_private *i915)
>   	struct intel_engine_cs *engine;
>   	enum intel_engine_id id;
>   
> -	lockdep_assert_held(&i915->drm.struct_mutex);
> -
>   	do {
>   		for_each_engine(engine, i915, id)
>   			mock_engine_flush(engine);
> @@ -55,9 +53,7 @@ static void mock_device_release(struct drm_device *dev)
>   	struct intel_engine_cs *engine;
>   	enum intel_engine_id id;
>   
> -	mutex_lock(&i915->drm.struct_mutex);
>   	mock_device_flush(i915);
> -	mutex_unlock(&i915->drm.struct_mutex);
>   
>   	flush_work(&i915->gem.idle_work);
>   	i915_gem_drain_workqueue(i915);
> 

Essentially looks fine. Provisional, meaning keep it if you do some 
small tweaks:

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] 53+ messages in thread

* Re: [PATCH 18/21] drm/i915: Remove the GEM idle worker
  2019-09-02  4:03 ` [PATCH 18/21] drm/i915: Remove the GEM idle worker Chris Wilson
@ 2019-09-24 15:26   ` Tvrtko Ursulin
  0 siblings, 0 replies; 53+ messages in thread
From: Tvrtko Ursulin @ 2019-09-24 15:26 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 02/09/2019 05:03, Chris Wilson wrote:
> Nothing inside the idle worker now requires struct_mutex, so we can
> remove the indirection of using our own worker.
> 
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> ---
>   drivers/gpu/drm/i915/gem/i915_gem_pm.c        | 28 ++-----------------
>   .../drm/i915/gem/selftests/i915_gem_mman.c    |  3 --
>   drivers/gpu/drm/i915/i915_debugfs.c           |  5 ----
>   drivers/gpu/drm/i915/i915_drv.h               |  9 ------
>   .../gpu/drm/i915/selftests/mock_gem_device.c  |  6 ----
>   5 files changed, 2 insertions(+), 49 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_pm.c b/drivers/gpu/drm/i915/gem/i915_gem_pm.c
> index fec0b410d1d9..e83eed8fa452 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_pm.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_pm.c
> @@ -13,36 +13,13 @@
>   
>   static void i915_gem_park(struct drm_i915_private *i915)
>   {
> -	lockdep_assert_held(&i915->drm.struct_mutex);
> +	cancel_delayed_work(&i915->gem.retire_work);
>   
>   	i915_vma_parked(i915);
>   
>   	i915_globals_park();
>   }
>   
> -static void idle_work_handler(struct work_struct *work)
> -{
> -	struct drm_i915_private *i915 =
> -		container_of(work, typeof(*i915), gem.idle_work);
> -	bool park;
> -
> -	cancel_delayed_work_sync(&i915->gem.retire_work);
> -	mutex_lock(&i915->drm.struct_mutex);
> -
> -	intel_wakeref_lock(&i915->gt.wakeref);
> -	park = (!intel_wakeref_is_active(&i915->gt.wakeref) &&
> -		!work_pending(work));
> -	intel_wakeref_unlock(&i915->gt.wakeref);
> -	if (park)
> -		i915_gem_park(i915);
> -	else
> -		queue_delayed_work(i915->wq,
> -				   &i915->gem.retire_work,
> -				   round_jiffies_up_relative(HZ));
> -
> -	mutex_unlock(&i915->drm.struct_mutex);
> -}
> -
>   static void retire_work_handler(struct work_struct *work)
>   {
>   	struct drm_i915_private *i915 =
> @@ -71,7 +48,7 @@ static int pm_notifier(struct notifier_block *nb,
>   		break;
>   
>   	case INTEL_GT_PARK:
> -		queue_work(i915->wq, &i915->gem.idle_work);
> +		i915_gem_park(i915);
>   		break;
>   	}
>   
> @@ -244,7 +221,6 @@ void i915_gem_resume(struct drm_i915_private *i915)
>   
>   void i915_gem_init__pm(struct drm_i915_private *i915)
>   {
> -	INIT_WORK(&i915->gem.idle_work, idle_work_handler);
>   	INIT_DELAYED_WORK(&i915->gem.retire_work, retire_work_handler);
>   
>   	i915->gem.pm_notifier.notifier_call = pm_notifier;
> 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 39c01bc4eb51..8563af1819c4 100644
> --- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c
> +++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c
> @@ -384,11 +384,8 @@ static bool assert_mmap_offset(struct drm_i915_private *i915,
>   static void disable_retire_worker(struct drm_i915_private *i915)
>   {
>   	i915_gem_driver_unregister__shrinker(i915);
> -
>   	intel_gt_pm_get(&i915->gt);
> -
>   	cancel_delayed_work_sync(&i915->gem.retire_work);
> -	flush_work(&i915->gem.idle_work);
>   }
>   
>   static void restore_retire_worker(struct drm_i915_private *i915)
> diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
> index 55f0fc03aa3e..09c6c485a732 100644
> --- a/drivers/gpu/drm/i915/i915_debugfs.c
> +++ b/drivers/gpu/drm/i915/i915_debugfs.c
> @@ -3642,11 +3642,6 @@ i915_drop_caches_set(void *data, u64 val)
>   		i915_gem_shrink_all(i915);
>   	fs_reclaim_release(GFP_KERNEL);
>   
> -	if (val & DROP_IDLE) {
> -		flush_delayed_work(&i915->gem.retire_work);
> -		flush_work(&i915->gem.idle_work);
> -	}
> -
>   	if (val & DROP_FREED)
>   		i915_gem_drain_freed_objects(i915);
>   
> diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
> index db7480831e52..b33fc7972e6b 100644
> --- a/drivers/gpu/drm/i915/i915_drv.h
> +++ b/drivers/gpu/drm/i915/i915_drv.h
> @@ -1718,15 +1718,6 @@ struct drm_i915_private {
>   		 * fires, go retire requests.
>   		 */
>   		struct delayed_work retire_work;
> -
> -		/**
> -		 * When we detect an idle GPU, we want to turn on
> -		 * powersaving features. So once we see that there
> -		 * are no more requests outstanding and no more
> -		 * arrive within a small period of time, we fire
> -		 * off the idle_work.
> -		 */
> -		struct work_struct idle_work;
>   	} gem;
>   
>   	/* For i945gm vblank irq vs. C3 workaround */
> diff --git a/drivers/gpu/drm/i915/selftests/mock_gem_device.c b/drivers/gpu/drm/i915/selftests/mock_gem_device.c
> index 1956006a0d5b..f3e9b5d7d098 100644
> --- a/drivers/gpu/drm/i915/selftests/mock_gem_device.c
> +++ b/drivers/gpu/drm/i915/selftests/mock_gem_device.c
> @@ -55,7 +55,6 @@ static void mock_device_release(struct drm_device *dev)
>   
>   	mock_device_flush(i915);
>   
> -	flush_work(&i915->gem.idle_work);
>   	i915_gem_drain_workqueue(i915);
>   
>   	mutex_lock(&i915->drm.struct_mutex);
> @@ -103,10 +102,6 @@ static void mock_retire_work_handler(struct work_struct *work)
>   {
>   }
>   
> -static void mock_idle_work_handler(struct work_struct *work)
> -{
> -}
> -
>   static int pm_domain_resume(struct device *dev)
>   {
>   	return pm_generic_runtime_resume(dev);
> @@ -186,7 +181,6 @@ struct drm_i915_private *mock_gem_device(void)
>   	mock_init_contexts(i915);
>   
>   	INIT_DELAYED_WORK(&i915->gem.retire_work, mock_retire_work_handler);
> -	INIT_WORK(&i915->gem.idle_work, mock_idle_work_handler);
>   
>   	i915->gt.awake = true;
>   
> 

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] 53+ messages in thread

* Re: [PATCH 19/21] drm/i915: Merge wait_for_timelines with retire_request
  2019-09-02  4:03 ` [PATCH 19/21] drm/i915: Merge wait_for_timelines with retire_request Chris Wilson
@ 2019-09-24 15:57   ` Tvrtko Ursulin
  2019-09-25  8:54     ` Chris Wilson
  0 siblings, 1 reply; 53+ messages in thread
From: Tvrtko Ursulin @ 2019-09-24 15:57 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 02/09/2019 05:03, Chris Wilson wrote:
> wait_for_timelines is essentially the same loop as retiring requests
> (with an extra), so merge the two into one routine.

Extra suspense! :)

> 
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> ---
>   drivers/gpu/drm/i915/gem/i915_gem_mman.c      |  4 +-
>   drivers/gpu/drm/i915/gem/i915_gem_pm.c        |  6 +-
>   .../drm/i915/gem/selftests/i915_gem_context.c |  4 +-
>   drivers/gpu/drm/i915/gt/selftest_timeline.c   |  2 +-
>   drivers/gpu/drm/i915/i915_debugfs.c           |  6 +-
>   drivers/gpu/drm/i915/i915_drv.h               |  3 +-
>   drivers/gpu/drm/i915/i915_gem.c               | 68 ++-----------------
>   drivers/gpu/drm/i915/i915_gem_evict.c         | 12 ++--
>   drivers/gpu/drm/i915/i915_gem_gtt.c           |  2 +-
>   drivers/gpu/drm/i915/i915_request.c           | 21 +++++-
>   drivers/gpu/drm/i915/i915_request.h           |  3 +-
>   .../gpu/drm/i915/selftests/igt_flush_test.c   |  4 +-
>   .../gpu/drm/i915/selftests/igt_live_test.c    |  4 +-
>   .../gpu/drm/i915/selftests/mock_gem_device.c  |  2 +-
>   14 files changed, 42 insertions(+), 99 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_mman.c b/drivers/gpu/drm/i915/gem/i915_gem_mman.c
> index 9a8c307c5aeb..761ab0076a6a 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_mman.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_mman.c
> @@ -429,9 +429,7 @@ static int create_mmap_offset(struct drm_i915_gem_object *obj)
>   
>   	/* Attempt to reap some mmap space from dead objects */
>   	do {
> -		err = i915_gem_wait_for_idle(i915,
> -					     I915_WAIT_INTERRUPTIBLE,
> -					     MAX_SCHEDULE_TIMEOUT);
> +		err = i915_gem_wait_for_idle(i915, MAX_SCHEDULE_TIMEOUT);
>   		if (err)
>   			break;
>   
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_pm.c b/drivers/gpu/drm/i915/gem/i915_gem_pm.c
> index e83eed8fa452..afbcf9219267 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_pm.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_pm.c
> @@ -25,7 +25,7 @@ static void retire_work_handler(struct work_struct *work)
>   	struct drm_i915_private *i915 =
>   		container_of(work, typeof(*i915), gem.retire_work.work);
>   
> -	i915_retire_requests(i915);
> +	i915_retire_requests(i915, 0);

Majority of callers end up with ", 0" which looks a bit aesthetically 
not pleasing. How about you add __i915_retire_requests(i915, timeout) 
instead for those few callers which need it? Or 
i915_retire_requests_wait/sync/timeout?

>   
>   	queue_delayed_work(i915->wq,
>   			   &i915->gem.retire_work,
> @@ -59,9 +59,7 @@ static bool switch_to_kernel_context_sync(struct intel_gt *gt)
>   {
>   	bool result = !intel_gt_is_wedged(gt);
>   
> -	if (i915_gem_wait_for_idle(gt->i915,
> -				   I915_WAIT_FOR_IDLE_BOOST,
> -				   I915_GEM_IDLE_TIMEOUT) == -ETIME) {
> +	if (i915_gem_wait_for_idle(gt->i915, I915_GEM_IDLE_TIMEOUT) == -ETIME) {

This now ends up interruptible sleep on a driver load path (and probably 
others) and I am not sure if that is okay. How about we keep the 
interruptible annotation?

>   		/* XXX hide warning from gem_eio */
>   		if (i915_modparams.reset) {
>   			dev_err(gt->i915->drm.dev,
> 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 b87e35a713b8..bc4c8d763024 100644
> --- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c
> +++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c
> @@ -304,7 +304,7 @@ create_test_object(struct i915_address_space *vm,
>   	int err;
>   
>   	/* Keep in GEM's good graces */
> -	i915_retire_requests(vm->i915);
> +	i915_retire_requests(vm->i915, 0);
>   
>   	size = min(vm->total / 2, 1024ull * DW_PER_PAGE * PAGE_SIZE);
>   	size = round_down(size, DW_PER_PAGE * PAGE_SIZE);
> @@ -923,7 +923,7 @@ __sseu_finish(const char *name,
>   
>   	if ((flags & TEST_IDLE) && ret == 0) {
>   		ret = i915_gem_wait_for_idle(ce->engine->i915,
> -					     0, MAX_SCHEDULE_TIMEOUT);
> +					     MAX_SCHEDULE_TIMEOUT);
>   		if (ret)
>   			return ret;
>   
> diff --git a/drivers/gpu/drm/i915/gt/selftest_timeline.c b/drivers/gpu/drm/i915/gt/selftest_timeline.c
> index 16abfabf08c7..b0b0fa5f91de 100644
> --- a/drivers/gpu/drm/i915/gt/selftest_timeline.c
> +++ b/drivers/gpu/drm/i915/gt/selftest_timeline.c
> @@ -734,7 +734,7 @@ static int live_hwsp_wrap(void *arg)
>   			goto out;
>   		}
>   
> -		i915_retire_requests(i915); /* recycle HWSP */
> +		i915_retire_requests(i915, 0); /* recycle HWSP */
>   	}
>   
>   out:
> diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
> index 09c6c485a732..d7410f3f576f 100644
> --- a/drivers/gpu/drm/i915/i915_debugfs.c
> +++ b/drivers/gpu/drm/i915/i915_debugfs.c
> @@ -3612,12 +3612,10 @@ i915_drop_caches_set(void *data, u64 val)
>   		intel_gt_set_wedged(&i915->gt);
>   
>   	if (val & DROP_RETIRE)
> -		i915_retire_requests(i915);
> +		i915_retire_requests(i915, 0);
>   
>   	if (val & (DROP_IDLE | DROP_ACTIVE)) {
> -		ret = i915_gem_wait_for_idle(i915,
> -					     I915_WAIT_INTERRUPTIBLE,
> -					     MAX_SCHEDULE_TIMEOUT);
> +		ret = i915_gem_wait_for_idle(i915, MAX_SCHEDULE_TIMEOUT);
>   		if (ret)
>   			return ret;
>   	}
> diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
> index b33fc7972e6b..3d1d652431be 100644
> --- a/drivers/gpu/drm/i915/i915_drv.h
> +++ b/drivers/gpu/drm/i915/i915_drv.h
> @@ -2314,8 +2314,7 @@ void i915_gem_driver_register(struct drm_i915_private *i915);
>   void i915_gem_driver_unregister(struct drm_i915_private *i915);
>   void i915_gem_driver_remove(struct drm_i915_private *dev_priv);
>   void i915_gem_driver_release(struct drm_i915_private *dev_priv);
> -int i915_gem_wait_for_idle(struct drm_i915_private *dev_priv,
> -			   unsigned int flags, long timeout);
> +int i915_gem_wait_for_idle(struct drm_i915_private *dev_priv, long timeout);
>   void i915_gem_suspend(struct drm_i915_private *dev_priv);
>   void i915_gem_suspend_late(struct drm_i915_private *dev_priv);
>   void i915_gem_resume(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 225fd22af858..c5f1c2043f97 100644
> --- a/drivers/gpu/drm/i915/i915_gem.c
> +++ b/drivers/gpu/drm/i915/i915_gem.c
> @@ -890,79 +890,19 @@ void i915_gem_runtime_suspend(struct drm_i915_private *i915)
>   	}
>   }
>   
> -static long
> -wait_for_timelines(struct drm_i915_private *i915,
> -		   unsigned int wait, long timeout)
> -{
> -	struct intel_gt_timelines *timelines = &i915->gt.timelines;
> -	struct intel_timeline *tl;
> -	unsigned long flags;
> -
> -	spin_lock_irqsave(&timelines->lock, flags);
> -	list_for_each_entry(tl, &timelines->active_list, link) {
> -		struct dma_fence *fence;
> -
> -		fence = i915_active_fence_get(&tl->last_request);
> -		if (!fence)
> -			continue;
> -
> -		spin_unlock_irqrestore(&timelines->lock, flags);
> -
> -		if (!dma_fence_is_i915(fence)) {
> -			timeout = dma_fence_wait_timeout(fence,
> -							 flags & I915_WAIT_INTERRUPTIBLE,
> -							 timeout);
> -		} else {
> -			struct i915_request *rq = to_request(fence);
> -
> -			/*
> -			 * "Race-to-idle".
> -			 *
> -			 * Switching to the kernel context is often used as
> -			 * a synchronous step prior to idling, e.g. in suspend
> -			 * for flushing all current operations to memory before
> -			 * sleeping. These we want to complete as quickly as
> -			 * possible to avoid prolonged stalls, so allow the gpu
> -			 * to boost to maximum clocks.
> -			 */
> -			if (flags & I915_WAIT_FOR_IDLE_BOOST)
> -				gen6_rps_boost(rq);
> -
> -			timeout = i915_request_wait(rq, flags, timeout);
> -		}
> -
> -		dma_fence_put(fence);
> -		if (timeout < 0)
> -			return timeout;
> -
> -		/* restart after reacquiring the lock */
> -		spin_lock_irqsave(&timelines->lock, flags);
> -		tl = list_entry(&timelines->active_list, typeof(*tl), link);
> -	}
> -	spin_unlock_irqrestore(&timelines->lock, flags);
> -
> -	return timeout;
> -}
> -
> -int i915_gem_wait_for_idle(struct drm_i915_private *i915,
> -			   unsigned int flags, long timeout)
> +int i915_gem_wait_for_idle(struct drm_i915_private *i915, long timeout)
>   {
>   	/* If the device is asleep, we have no requests outstanding */
>   	if (!intel_gt_pm_is_awake(&i915->gt))
>   		return 0;
>   
> -	do {
> -		timeout = wait_for_timelines(i915, flags, timeout);
> -		if (timeout < 0)
> -			return timeout;
> -
> +	while ((timeout = i915_retire_requests(i915, timeout)) > 0) {
>   		cond_resched();
>   		if (signal_pending(current))
>   			return -EINTR;
> +	}
>   
> -	} while (i915_retire_requests(i915));
> -
> -	return 0;
> +	return timeout;
>   }
>   
>   struct i915_vma *
> diff --git a/drivers/gpu/drm/i915/i915_gem_evict.c b/drivers/gpu/drm/i915/i915_gem_evict.c
> index 44f5b638fa43..708055a3887e 100644
> --- a/drivers/gpu/drm/i915/i915_gem_evict.c
> +++ b/drivers/gpu/drm/i915/i915_gem_evict.c
> @@ -46,9 +46,7 @@ static int ggtt_flush(struct drm_i915_private *i915)
>   	 * the hopes that we can then remove contexts and the like only
>   	 * bound by their active reference.
>   	 */
> -	return i915_gem_wait_for_idle(i915,
> -				      I915_WAIT_INTERRUPTIBLE,
> -				      MAX_SCHEDULE_TIMEOUT);
> +	return i915_gem_wait_for_idle(i915, MAX_SCHEDULE_TIMEOUT);
>   }
>   
>   static bool
> @@ -126,6 +124,8 @@ i915_gem_evict_something(struct i915_address_space *vm,
>   				    min_size, alignment, cache_level,
>   				    start, end, mode);
>   
> +	i915_retire_requests(vm->i915, 0);
> +
>   search_again:
>   	active = NULL;
>   	INIT_LIST_HEAD(&eviction_list);
> @@ -265,13 +265,13 @@ int i915_gem_evict_for_node(struct i915_address_space *vm,
>   
>   	trace_i915_gem_evict_node(vm, target, flags);
>   
> -	/* Retire before we search the active list. Although we have
> +	/*
> +	 * Retire before we search the active list. Although we have
>   	 * reasonable accuracy in our retirement lists, we may have
>   	 * a stray pin (preventing eviction) that can only be resolved by
>   	 * retiring.
>   	 */
> -	if (!(flags & PIN_NONBLOCK))
> -		i915_retire_requests(vm->i915);
> +	i915_retire_requests(vm->i915, 0);
>   
>   	check_color = vm->mm.color_adjust;
>   	if (check_color) {
> diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
> index 60676de059a7..2b7a4d49b2e6 100644
> --- a/drivers/gpu/drm/i915/i915_gem_gtt.c
> +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
> @@ -2524,7 +2524,7 @@ void i915_gem_gtt_finish_pages(struct drm_i915_gem_object *obj,
>   	struct i915_ggtt *ggtt = &dev_priv->ggtt;
>   
>   	if (unlikely(ggtt->do_idle_maps)) {
> -		if (i915_gem_wait_for_idle(dev_priv, 0, MAX_SCHEDULE_TIMEOUT)) {
> +		if (i915_retire_requests(dev_priv, MAX_SCHEDULE_TIMEOUT)) {

Why this couldn't state i915_gem_wait_for_idle?

>   			DRM_ERROR("Failed to wait for idle; VT'd may hang.\n");
>   			/* Wait a bit, in hopes it avoids the hang */
>   			udelay(10);
> diff --git a/drivers/gpu/drm/i915/i915_request.c b/drivers/gpu/drm/i915/i915_request.c
> index 4ecfae143276..1c5e804c9ca2 100644
> --- a/drivers/gpu/drm/i915/i915_request.c
> +++ b/drivers/gpu/drm/i915/i915_request.c
> @@ -1429,10 +1429,11 @@ long i915_request_wait(struct i915_request *rq,
>   	return timeout;
>   }
>   
> -bool i915_retire_requests(struct drm_i915_private *i915)
> +long i915_retire_requests(struct drm_i915_private *i915, long timeout)
>   {
>   	struct intel_gt_timelines *timelines = &i915->gt.timelines;
>   	struct intel_timeline *tl, *tn;
> +	unsigned long active_count = 0;
>   	unsigned long flags;
>   	LIST_HEAD(free);
>   
> @@ -1446,13 +1447,27 @@ bool i915_retire_requests(struct drm_i915_private *i915)
>   		tl->active_count++; /* pin the list element */
>   		spin_unlock_irqrestore(&timelines->lock, flags);
>   
> +		if (timeout > 0) {
> +			struct dma_fence *fence;
> +
> +			fence = i915_active_fence_get(&tl->last_request);
> +			if (fence) {
> +				timeout = dma_fence_wait_timeout(fence,
> +								 true,
> +								 timeout);
> +				dma_fence_put(fence);
> +			}
> +		}
> +
>   		retire_requests(tl);
>   
>   		spin_lock_irqsave(&timelines->lock, flags);
>   
>   		/* Resume iteration after dropping lock */
>   		list_safe_reset_next(tl, tn, link);
> -		if (!--tl->active_count)
> +		if (--tl->active_count)
> +			active_count += !!rcu_access_pointer(tl->last_request.fence);
> +		else
>   			list_del(&tl->link);
>   
>   		mutex_unlock(&tl->mutex);
> @@ -1468,7 +1483,7 @@ bool i915_retire_requests(struct drm_i915_private *i915)
>   	list_for_each_entry_safe(tl, tn, &free, link)
>   		__intel_timeline_free(&tl->kref);
>   
> -	return !list_empty(&timelines->active_list);
> +	return active_count ? timeout : 0;
>   }
>   
>   #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
> diff --git a/drivers/gpu/drm/i915/i915_request.h b/drivers/gpu/drm/i915/i915_request.h
> index 57a2193c64d1..2a5d682aa6b1 100644
> --- a/drivers/gpu/drm/i915/i915_request.h
> +++ b/drivers/gpu/drm/i915/i915_request.h
> @@ -310,7 +310,6 @@ long i915_request_wait(struct i915_request *rq,
>   #define I915_WAIT_INTERRUPTIBLE	BIT(0)
>   #define I915_WAIT_PRIORITY	BIT(1) /* small priority bump for the request */
>   #define I915_WAIT_ALL		BIT(2) /* used by i915_gem_object_wait() */
> -#define I915_WAIT_FOR_IDLE_BOOST BIT(3)
>   
>   static inline bool i915_request_signaled(const struct i915_request *rq)
>   {
> @@ -440,6 +439,6 @@ static inline bool i915_request_has_nopreempt(const struct i915_request *rq)
>   	return unlikely(rq->flags & I915_REQUEST_NOPREEMPT);
>   }
>   
> -bool i915_retire_requests(struct drm_i915_private *i915);
> +long i915_retire_requests(struct drm_i915_private *i915, long timeout);
>   
>   #endif /* I915_REQUEST_H */
> diff --git a/drivers/gpu/drm/i915/selftests/igt_flush_test.c b/drivers/gpu/drm/i915/selftests/igt_flush_test.c
> index 2a5fbe46ea9f..ed496bd6d84f 100644
> --- a/drivers/gpu/drm/i915/selftests/igt_flush_test.c
> +++ b/drivers/gpu/drm/i915/selftests/igt_flush_test.c
> @@ -18,8 +18,7 @@ int igt_flush_test(struct drm_i915_private *i915)
>   
>   	cond_resched();
>   
> -	i915_retire_requests(i915);
> -	if (i915_gem_wait_for_idle(i915, 0, HZ / 5) == -ETIME) {
> +	if (i915_gem_wait_for_idle(i915, HZ / 5) == -ETIME) {
>   		pr_err("%pS timed out, cancelling all further testing.\n",
>   		       __builtin_return_address(0));
>   
> @@ -30,7 +29,6 @@ int igt_flush_test(struct drm_i915_private *i915)
>   		intel_gt_set_wedged(&i915->gt);
>   		ret = -EIO;
>   	}
> -	i915_retire_requests(i915);
>   
>   	return ret;
>   }
> diff --git a/drivers/gpu/drm/i915/selftests/igt_live_test.c b/drivers/gpu/drm/i915/selftests/igt_live_test.c
> index 04a6f88fdf64..eae90f97df6c 100644
> --- a/drivers/gpu/drm/i915/selftests/igt_live_test.c
> +++ b/drivers/gpu/drm/i915/selftests/igt_live_test.c
> @@ -23,9 +23,7 @@ int igt_live_test_begin(struct igt_live_test *t,
>   	t->func = func;
>   	t->name = name;
>   
> -	err = i915_gem_wait_for_idle(i915,
> -				     I915_WAIT_INTERRUPTIBLE,
> -				     MAX_SCHEDULE_TIMEOUT);
> +	err = i915_gem_wait_for_idle(i915, MAX_SCHEDULE_TIMEOUT);
>   	if (err) {
>   		pr_err("%s(%s): failed to idle before, with err=%d!",
>   		       func, name, err);
> diff --git a/drivers/gpu/drm/i915/selftests/mock_gem_device.c b/drivers/gpu/drm/i915/selftests/mock_gem_device.c
> index f3e9b5d7d098..66cc5634db1c 100644
> --- a/drivers/gpu/drm/i915/selftests/mock_gem_device.c
> +++ b/drivers/gpu/drm/i915/selftests/mock_gem_device.c
> @@ -44,7 +44,7 @@ void mock_device_flush(struct drm_i915_private *i915)
>   	do {
>   		for_each_engine(engine, i915, id)
>   			mock_engine_flush(engine);
> -	} while (i915_retire_requests(i915));
> +	} while (i915_retire_requests(i915, MAX_SCHEDULE_TIMEOUT));
>   }
>   
>   static void mock_device_release(struct drm_device *dev)
> 
Regards,

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

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

* Re: [PATCH 12/21] drm/i915: Mark up address spaces that may need to allocate
  2019-09-23  8:10       ` Tvrtko Ursulin
@ 2019-09-25  8:23         ` Chris Wilson
  2019-09-25 15:59           ` Tvrtko Ursulin
  0 siblings, 1 reply; 53+ messages in thread
From: Chris Wilson @ 2019-09-25  8:23 UTC (permalink / raw)
  To: Tvrtko Ursulin, intel-gfx

Quoting Tvrtko Ursulin (2019-09-23 09:10:26)
> 
> On 20/09/2019 17:35, Chris Wilson wrote:
> > Quoting Tvrtko Ursulin (2019-09-20 17:22:42)
> >>
> >> On 02/09/2019 05:02, Chris Wilson wrote:
> >>> Since we cannot allocate underneath the vm->mutex (it is used in the
> >>> direct-reclaim paths), we need to shift the allocations off into a
> >>> mutexless worker with fence recursion prevention. To know when we need
> >>> this protection, we mark up the address spaces that do allocate before
> >>> insertion.
> >>>
> >>> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> >>> ---
> >>>    drivers/gpu/drm/i915/i915_gem_gtt.c | 3 +++
> >>>    drivers/gpu/drm/i915/i915_gem_gtt.h | 2 ++
> >>>    2 files changed, 5 insertions(+)
> >>>
> >>> diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
> >>> index 9095f017162e..56d27cf09a3d 100644
> >>> --- a/drivers/gpu/drm/i915/i915_gem_gtt.c
> >>> +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
> >>> @@ -1500,6 +1500,7 @@ static struct i915_ppgtt *gen8_ppgtt_create(struct drm_i915_private *i915)
> >>>                        goto err_free_pd;
> >>>        }
> >>>    
> >>> +     ppgtt->vm.bind_alloc = I915_VMA_LOCAL_BIND;
> >>
> >> So this is re-using I915_VMA_LOCAL_BIND as a trick? Is it clear how that
> >> works from these call sites? Should it be called bind_alloc*s*?
> >> bind_allocates? Or be a boolean which is converted to a trick flag in
> >> i915_vma_bind where a comment can be put explaining the trick?
> > 
> > Is it a trick? We need to differentiate between requests for LOCAL_BIND,
> > GLOBAL_BIND, LOCAL_BIND | GLOBAL_BIND, for different types of vm. Then I
> > have a plan on using the worker for GLOBAL_BIND on bsw/bxt to defer the
> > stop_machine().
> 
> What's the connection between "mark up the address spaces that do 
> allocate before insertion" and I915_VMA_LOCAL_BIND?

Full-ppgtt is only accessible by PIN_USER.

Aliasing-ppgtt is accessible from global-gtt as PIN_USER. Only if we
have an aliasing-gtt behind ggtt do we want to allocate for ggtt for
local binds.

global-gtt by itself never allocates and is expected to be synchronous.
However, we do use stop_machine() for bxt/bsw and that unfortunately is
marked as an allocating mutex so one idea I had for avoiding that
lockdep splat was to make bxt/bsw PIN_GLOBAL async.
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* Re: [PATCH 17/21] drm/i915: Drop struct_mutex from around i915_retire_requests()
  2019-09-24 15:25   ` Tvrtko Ursulin
@ 2019-09-25  8:43     ` Chris Wilson
  2019-09-25  8:49       ` Tvrtko Ursulin
  0 siblings, 1 reply; 53+ messages in thread
From: Chris Wilson @ 2019-09-25  8:43 UTC (permalink / raw)
  To: Tvrtko Ursulin, intel-gfx

Quoting Tvrtko Ursulin (2019-09-24 16:25:29)
> 
> On 02/09/2019 05:02, Chris Wilson wrote:
> > @@ -449,8 +447,6 @@ tl_write(struct intel_timeline *tl, struct intel_engine_cs *engine, u32 value)
> >       struct i915_request *rq;
> >       int err;
> >   
> > -     lockdep_assert_held(&tl->gt->i915->drm.struct_mutex); /* lazy rq refs */
> > -
> >       err = intel_timeline_pin(tl);
> >       if (err) {
> >               rq = ERR_PTR(err);
> > @@ -461,10 +457,14 @@ tl_write(struct intel_timeline *tl, struct intel_engine_cs *engine, u32 value)
> >       if (IS_ERR(rq))
> >               goto out_unpin;
> >   
> > +     i915_request_get(rq);
> > +
> >       err = emit_ggtt_store_dw(rq, tl->hwsp_offset, value);
> >       i915_request_add(rq);
> > -     if (err)
> > +     if (err) {
> > +             i915_request_put(rq);
> >               rq = ERR_PTR(err);
> > +     }
> >   
> >   out_unpin:
> >       intel_timeline_unpin(tl);
> > @@ -500,7 +500,6 @@ static int live_hwsp_engine(void *arg)
> >       struct intel_timeline **timelines;
> >       struct intel_engine_cs *engine;
> >       enum intel_engine_id id;
> > -     intel_wakeref_t wakeref;
> >       unsigned long count, n;
> >       int err = 0;
> >   
> > @@ -515,14 +514,13 @@ static int live_hwsp_engine(void *arg)
> >       if (!timelines)
> >               return -ENOMEM;
> >   
> > -     mutex_lock(&i915->drm.struct_mutex);
> > -     wakeref = intel_runtime_pm_get(&i915->runtime_pm);
> > -
> >       count = 0;
> >       for_each_engine(engine, i915, id) {
> >               if (!intel_engine_can_store_dword(engine))
> >                       continue;
> >   
> > +             intel_engine_pm_get(engine);
> > +
> >               for (n = 0; n < NUM_TIMELINES; n++) {
> >                       struct intel_timeline *tl;
> >                       struct i915_request *rq;
> > @@ -530,22 +528,26 @@ static int live_hwsp_engine(void *arg)
> >                       tl = checked_intel_timeline_create(i915);
> >                       if (IS_ERR(tl)) {
> >                               err = PTR_ERR(tl);
> > -                             goto out;
> > +                             break;
> >                       }
> >   
> >                       rq = tl_write(tl, engine, count);
> >                       if (IS_ERR(rq)) {
> >                               intel_timeline_put(tl);
> >                               err = PTR_ERR(rq);
> > -                             goto out;
> > +                             break;
> >                       }
> >   
> >                       timelines[count++] = tl;
> > +                     i915_request_put(rq);
> 
> This was a leak until now?

No, we added a get into tl_write() so that we own a reference to the
request, just so that ownership is clear across the waits (and it can't
accidentally be retired).

> > @@ -681,7 +670,9 @@ static int live_hwsp_wrap(void *arg)
> >               if (!intel_engine_can_store_dword(engine))
> >                       continue;
> >   
> > +             intel_engine_pm_get(engine);
> >               rq = i915_request_create(engine->kernel_context);
> > +             intel_engine_pm_put(engine);
> >               if (IS_ERR(rq)) {
> >                       err = PTR_ERR(rq);
> >                       goto out;
> > @@ -747,16 +738,12 @@ static int live_hwsp_wrap(void *arg)
> >       }
> >   
> 
> No i915_request_put in this one and I can't see why it would be different.

It's not using tl_write with the added ref.

> > diff --git a/drivers/gpu/drm/i915/selftests/igt_flush_test.c b/drivers/gpu/drm/i915/selftests/igt_flush_test.c
> > index d3b5eb402d33..2a5fbe46ea9f 100644
> > --- a/drivers/gpu/drm/i915/selftests/igt_flush_test.c
> > +++ b/drivers/gpu/drm/i915/selftests/igt_flush_test.c
> > @@ -12,31 +12,25 @@
> >   
> >   #include "igt_flush_test.h"
> >   
> > -int igt_flush_test(struct drm_i915_private *i915, unsigned int flags)
> > +int igt_flush_test(struct drm_i915_private *i915)
> >   {
> >       int ret = intel_gt_is_wedged(&i915->gt) ? -EIO : 0;
> > -     int repeat = !!(flags & I915_WAIT_LOCKED);
> >   
> >       cond_resched();
> >   
> > -     do {
> > -             if (i915_gem_wait_for_idle(i915, flags, HZ / 5) == -ETIME) {
> > -                     pr_err("%pS timed out, cancelling all further testing.\n",
> > -                            __builtin_return_address(0));
> > +     i915_retire_requests(i915);
> 
> Do you need this one or it would be better without, for clarity, given 
> there is one at the end? i915_gem_wait_for_idle will retire all it can.

It's to aide kick starting of the idle-barrier. Eventually this is
pulled together again.

> > +     if (i915_gem_wait_for_idle(i915, 0, HZ / 5) == -ETIME) {
> > +             pr_err("%pS timed out, cancelling all further testing.\n",
> > +                    __builtin_return_address(0));
> >   
> > -                     GEM_TRACE("%pS timed out.\n",
> > -                               __builtin_return_address(0));
> > -                     GEM_TRACE_DUMP();
> > +             GEM_TRACE("%pS timed out.\n",
> > +                       __builtin_return_address(0));
> > +             GEM_TRACE_DUMP();
> >   
> > -                     intel_gt_set_wedged(&i915->gt);
> > -                     repeat = 0;
> > -                     ret = -EIO;
> > -             }
> > -
> > -             /* Ensure we also flush after wedging. */
> > -             if (flags & I915_WAIT_LOCKED)
> > -                     i915_retire_requests(i915);
> > -     } while (repeat--);
> > +             intel_gt_set_wedged(&i915->gt);
> > +             ret = -EIO;
> > +     }
> > +     i915_retire_requests(i915);
> >   
> >       return ret;
> >   }

> Essentially looks fine. Provisional, meaning keep it if you do some 
> small tweaks:

Nothing seemed to be required.
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* Re: [PATCH 17/21] drm/i915: Drop struct_mutex from around i915_retire_requests()
  2019-09-25  8:43     ` Chris Wilson
@ 2019-09-25  8:49       ` Tvrtko Ursulin
  0 siblings, 0 replies; 53+ messages in thread
From: Tvrtko Ursulin @ 2019-09-25  8:49 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 25/09/2019 09:43, Chris Wilson wrote:
> Quoting Tvrtko Ursulin (2019-09-24 16:25:29)
>>
>> On 02/09/2019 05:02, Chris Wilson wrote:
>>> @@ -449,8 +447,6 @@ tl_write(struct intel_timeline *tl, struct intel_engine_cs *engine, u32 value)
>>>        struct i915_request *rq;
>>>        int err;
>>>    
>>> -     lockdep_assert_held(&tl->gt->i915->drm.struct_mutex); /* lazy rq refs */
>>> -
>>>        err = intel_timeline_pin(tl);
>>>        if (err) {
>>>                rq = ERR_PTR(err);
>>> @@ -461,10 +457,14 @@ tl_write(struct intel_timeline *tl, struct intel_engine_cs *engine, u32 value)
>>>        if (IS_ERR(rq))
>>>                goto out_unpin;
>>>    
>>> +     i915_request_get(rq);
>>> +
>>>        err = emit_ggtt_store_dw(rq, tl->hwsp_offset, value);
>>>        i915_request_add(rq);
>>> -     if (err)
>>> +     if (err) {
>>> +             i915_request_put(rq);
>>>                rq = ERR_PTR(err);
>>> +     }
>>>    
>>>    out_unpin:
>>>        intel_timeline_unpin(tl);
>>> @@ -500,7 +500,6 @@ static int live_hwsp_engine(void *arg)
>>>        struct intel_timeline **timelines;
>>>        struct intel_engine_cs *engine;
>>>        enum intel_engine_id id;
>>> -     intel_wakeref_t wakeref;
>>>        unsigned long count, n;
>>>        int err = 0;
>>>    
>>> @@ -515,14 +514,13 @@ static int live_hwsp_engine(void *arg)
>>>        if (!timelines)
>>>                return -ENOMEM;
>>>    
>>> -     mutex_lock(&i915->drm.struct_mutex);
>>> -     wakeref = intel_runtime_pm_get(&i915->runtime_pm);
>>> -
>>>        count = 0;
>>>        for_each_engine(engine, i915, id) {
>>>                if (!intel_engine_can_store_dword(engine))
>>>                        continue;
>>>    
>>> +             intel_engine_pm_get(engine);
>>> +
>>>                for (n = 0; n < NUM_TIMELINES; n++) {
>>>                        struct intel_timeline *tl;
>>>                        struct i915_request *rq;
>>> @@ -530,22 +528,26 @@ static int live_hwsp_engine(void *arg)
>>>                        tl = checked_intel_timeline_create(i915);
>>>                        if (IS_ERR(tl)) {
>>>                                err = PTR_ERR(tl);
>>> -                             goto out;
>>> +                             break;
>>>                        }
>>>    
>>>                        rq = tl_write(tl, engine, count);
>>>                        if (IS_ERR(rq)) {
>>>                                intel_timeline_put(tl);
>>>                                err = PTR_ERR(rq);
>>> -                             goto out;
>>> +                             break;
>>>                        }
>>>    
>>>                        timelines[count++] = tl;
>>> +                     i915_request_put(rq);
>>
>> This was a leak until now?
> 
> No, we added a get into tl_write() so that we own a reference to the
> request, just so that ownership is clear across the waits (and it can't
> accidentally be retired).

/me scrolls up, scrolls down.. rubs eyes.. yep, I was blind.

[snip]

>> Essentially looks fine. Provisional, meaning keep it if you do some
>> small tweaks:
> 
> Nothing seemed to be required.

Ack.

Regards,

Tvrtko


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

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

* Re: [PATCH 19/21] drm/i915: Merge wait_for_timelines with retire_request
  2019-09-24 15:57   ` Tvrtko Ursulin
@ 2019-09-25  8:54     ` Chris Wilson
  0 siblings, 0 replies; 53+ messages in thread
From: Chris Wilson @ 2019-09-25  8:54 UTC (permalink / raw)
  To: Tvrtko Ursulin, intel-gfx

Quoting Tvrtko Ursulin (2019-09-24 16:57:12)
> 
> On 02/09/2019 05:03, Chris Wilson wrote:
> > wait_for_timelines is essentially the same loop as retiring requests
> > (with an extra), so merge the two into one routine.
> 
> Extra suspense! :)
> 
> > 
> > Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> > ---
> >   drivers/gpu/drm/i915/gem/i915_gem_mman.c      |  4 +-
> >   drivers/gpu/drm/i915/gem/i915_gem_pm.c        |  6 +-
> >   .../drm/i915/gem/selftests/i915_gem_context.c |  4 +-
> >   drivers/gpu/drm/i915/gt/selftest_timeline.c   |  2 +-
> >   drivers/gpu/drm/i915/i915_debugfs.c           |  6 +-
> >   drivers/gpu/drm/i915/i915_drv.h               |  3 +-
> >   drivers/gpu/drm/i915/i915_gem.c               | 68 ++-----------------
> >   drivers/gpu/drm/i915/i915_gem_evict.c         | 12 ++--
> >   drivers/gpu/drm/i915/i915_gem_gtt.c           |  2 +-
> >   drivers/gpu/drm/i915/i915_request.c           | 21 +++++-
> >   drivers/gpu/drm/i915/i915_request.h           |  3 +-
> >   .../gpu/drm/i915/selftests/igt_flush_test.c   |  4 +-
> >   .../gpu/drm/i915/selftests/igt_live_test.c    |  4 +-
> >   .../gpu/drm/i915/selftests/mock_gem_device.c  |  2 +-
> >   14 files changed, 42 insertions(+), 99 deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/i915/gem/i915_gem_mman.c b/drivers/gpu/drm/i915/gem/i915_gem_mman.c
> > index 9a8c307c5aeb..761ab0076a6a 100644
> > --- a/drivers/gpu/drm/i915/gem/i915_gem_mman.c
> > +++ b/drivers/gpu/drm/i915/gem/i915_gem_mman.c
> > @@ -429,9 +429,7 @@ static int create_mmap_offset(struct drm_i915_gem_object *obj)
> >   
> >       /* Attempt to reap some mmap space from dead objects */
> >       do {
> > -             err = i915_gem_wait_for_idle(i915,
> > -                                          I915_WAIT_INTERRUPTIBLE,
> > -                                          MAX_SCHEDULE_TIMEOUT);
> > +             err = i915_gem_wait_for_idle(i915, MAX_SCHEDULE_TIMEOUT);
> >               if (err)
> >                       break;
> >   
> > diff --git a/drivers/gpu/drm/i915/gem/i915_gem_pm.c b/drivers/gpu/drm/i915/gem/i915_gem_pm.c
> > index e83eed8fa452..afbcf9219267 100644
> > --- a/drivers/gpu/drm/i915/gem/i915_gem_pm.c
> > +++ b/drivers/gpu/drm/i915/gem/i915_gem_pm.c
> > @@ -25,7 +25,7 @@ static void retire_work_handler(struct work_struct *work)
> >       struct drm_i915_private *i915 =
> >               container_of(work, typeof(*i915), gem.retire_work.work);
> >   
> > -     i915_retire_requests(i915);
> > +     i915_retire_requests(i915, 0);
> 
> Majority of callers end up with ", 0" which looks a bit aesthetically 
> not pleasing. How about you add __i915_retire_requests(i915, timeout) 
> instead for those few callers which need it? Or 
> i915_retire_requests_wait/sync/timeout?
> 
> >   
> >       queue_delayed_work(i915->wq,
> >                          &i915->gem.retire_work,
> > @@ -59,9 +59,7 @@ static bool switch_to_kernel_context_sync(struct intel_gt *gt)
> >   {
> >       bool result = !intel_gt_is_wedged(gt);
> >   
> > -     if (i915_gem_wait_for_idle(gt->i915,
> > -                                I915_WAIT_FOR_IDLE_BOOST,
> > -                                I915_GEM_IDLE_TIMEOUT) == -ETIME) {
> > +     if (i915_gem_wait_for_idle(gt->i915, I915_GEM_IDLE_TIMEOUT) == -ETIME) {
> 
> This now ends up interruptible sleep on a driver load path (and probably 
> others) and I am not sure if that is okay. How about we keep the 
> interruptible annotation?

SIGINT during module load should be returned to the user, no?

The only one that is a pain is the broken vtd.

> > diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
> > index 60676de059a7..2b7a4d49b2e6 100644
> > --- a/drivers/gpu/drm/i915/i915_gem_gtt.c
> > +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
> > @@ -2524,7 +2524,7 @@ void i915_gem_gtt_finish_pages(struct drm_i915_gem_object *obj,
> >       struct i915_ggtt *ggtt = &dev_priv->ggtt;
> >   
> >       if (unlikely(ggtt->do_idle_maps)) {
> > -             if (i915_gem_wait_for_idle(dev_priv, 0, MAX_SCHEDULE_TIMEOUT)) {
> > +             if (i915_retire_requests(dev_priv, MAX_SCHEDULE_TIMEOUT)) {
> 
> Why this couldn't state i915_gem_wait_for_idle?

But there is no i915_gem_wait_for_idle, calling into GEM userspace layer
from here is a layering violation. (You might think this says
i915_gem_gtt.c, but it lies.)

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

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

* Re: [PATCH 21/21] drm/i915: Move global activity tracking from GEM to GT
  2019-09-02  4:03 ` [PATCH 21/21] drm/i915: Move global activity tracking from GEM to GT Chris Wilson
@ 2019-09-25  9:55   ` Tvrtko Ursulin
  0 siblings, 0 replies; 53+ messages in thread
From: Tvrtko Ursulin @ 2019-09-25  9:55 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 02/09/2019 05:03, Chris Wilson wrote:
> As our global unpark/park keep track of the number of active users, we
> can simply move the accounting from the GEM layer to the base GT layer.
> It was placed originally inside GEM to benefit from the 100ms extra
> delay on idleness, but that has been eliminated and now there is no
> substantive difference between the layers. In moving it, we move another
> piece of the puzzle out from underneath struct_mutex.
> 
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
> ---
>   drivers/gpu/drm/i915/gem/i915_gem_pm.c | 11 +----------
>   drivers/gpu/drm/i915/gt/intel_gt_pm.c  |  5 +++++
>   2 files changed, 6 insertions(+), 10 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_pm.c b/drivers/gpu/drm/i915/gem/i915_gem_pm.c
> index b459719386e3..5816bdb5bfa2 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_pm.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_pm.c
> @@ -10,14 +10,6 @@
>   #include "gt/intel_gt_requests.h"
>   
>   #include "i915_drv.h"
> -#include "i915_globals.h"
> -
> -static void i915_gem_park(struct drm_i915_private *i915)
> -{
> -	i915_vma_parked(i915);
> -
> -	i915_globals_park();
> -}
>   
>   static int pm_notifier(struct notifier_block *nb,
>   		       unsigned long action,
> @@ -28,11 +20,10 @@ static int pm_notifier(struct notifier_block *nb,
>   
>   	switch (action) {
>   	case INTEL_GT_UNPARK:
> -		i915_globals_unpark();
>   		break;
>   
>   	case INTEL_GT_PARK:
> -		i915_gem_park(i915);
> +		i915_vma_parked(i915);
>   		break;
>   	}
>   
> diff --git a/drivers/gpu/drm/i915/gt/intel_gt_pm.c b/drivers/gpu/drm/i915/gt/intel_gt_pm.c
> index fa96e1ad7bd8..d31ad2d63175 100644
> --- a/drivers/gpu/drm/i915/gt/intel_gt_pm.c
> +++ b/drivers/gpu/drm/i915/gt/intel_gt_pm.c
> @@ -5,6 +5,7 @@
>    */
>   
>   #include "i915_drv.h"
> +#include "i915_globals.h"
>   #include "i915_params.h"
>   #include "intel_context.h"
>   #include "intel_engine_pm.h"
> @@ -26,6 +27,8 @@ static int __gt_unpark(struct intel_wakeref *wf)
>   
>   	GEM_TRACE("\n");
>   
> +	i915_globals_unpark();
> +
>   	/*
>   	 * It seems that the DMC likes to transition between the DC states a lot
>   	 * when there are no connected displays (no active power domains) during
> @@ -77,6 +80,8 @@ static int __gt_park(struct intel_wakeref *wf)
>   	GEM_BUG_ON(!wakeref);
>   	intel_display_power_put(i915, POWER_DOMAIN_GT_IRQ, wakeref);
>   
> +	i915_globals_park();
> +
>   	return 0;
>   }
>   
> 

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] 53+ messages in thread

* Re: [PATCH 12/21] drm/i915: Mark up address spaces that may need to allocate
  2019-09-25  8:23         ` Chris Wilson
@ 2019-09-25 15:59           ` Tvrtko Ursulin
  2019-09-27 17:03             ` Chris Wilson
  0 siblings, 1 reply; 53+ messages in thread
From: Tvrtko Ursulin @ 2019-09-25 15:59 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 25/09/2019 09:23, Chris Wilson wrote:
> Quoting Tvrtko Ursulin (2019-09-23 09:10:26)
>>
>> On 20/09/2019 17:35, Chris Wilson wrote:
>>> Quoting Tvrtko Ursulin (2019-09-20 17:22:42)
>>>>
>>>> On 02/09/2019 05:02, Chris Wilson wrote:
>>>>> Since we cannot allocate underneath the vm->mutex (it is used in the
>>>>> direct-reclaim paths), we need to shift the allocations off into a
>>>>> mutexless worker with fence recursion prevention. To know when we need
>>>>> this protection, we mark up the address spaces that do allocate before
>>>>> insertion.
>>>>>
>>>>> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
>>>>> ---
>>>>>     drivers/gpu/drm/i915/i915_gem_gtt.c | 3 +++
>>>>>     drivers/gpu/drm/i915/i915_gem_gtt.h | 2 ++
>>>>>     2 files changed, 5 insertions(+)
>>>>>
>>>>> diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
>>>>> index 9095f017162e..56d27cf09a3d 100644
>>>>> --- a/drivers/gpu/drm/i915/i915_gem_gtt.c
>>>>> +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
>>>>> @@ -1500,6 +1500,7 @@ static struct i915_ppgtt *gen8_ppgtt_create(struct drm_i915_private *i915)
>>>>>                         goto err_free_pd;
>>>>>         }
>>>>>     
>>>>> +     ppgtt->vm.bind_alloc = I915_VMA_LOCAL_BIND;
>>>>
>>>> So this is re-using I915_VMA_LOCAL_BIND as a trick? Is it clear how that
>>>> works from these call sites? Should it be called bind_alloc*s*?
>>>> bind_allocates? Or be a boolean which is converted to a trick flag in
>>>> i915_vma_bind where a comment can be put explaining the trick?
>>>
>>> Is it a trick? We need to differentiate between requests for LOCAL_BIND,
>>> GLOBAL_BIND, LOCAL_BIND | GLOBAL_BIND, for different types of vm. Then I
>>> have a plan on using the worker for GLOBAL_BIND on bsw/bxt to defer the
>>> stop_machine().
>>
>> What's the connection between "mark up the address spaces that do
>> allocate before insertion" and I915_VMA_LOCAL_BIND?
> 
> Full-ppgtt is only accessible by PIN_USER.
> 
> Aliasing-ppgtt is accessible from global-gtt as PIN_USER. Only if we
> have an aliasing-gtt behind ggtt do we want to allocate for ggtt for
> local binds.
> 
> global-gtt by itself never allocates and is expected to be synchronous.
> However, we do use stop_machine() for bxt/bsw and that unfortunately is
> marked as an allocating mutex so one idea I had for avoiding that
> lockdep splat was to make bxt/bsw PIN_GLOBAL async.

I think we are not understanding each other from the very start.

My point was that "vm.bind_alloc = I915_VMA_LOCAL_BIND", at least my 
understanding, effectively means "use the worker when pinning/binding 
PIN_USER/I915_VMA_LOCAL_BIND". And that is I think non-obvious. Where 
you have in the code:

	if (flags & vma->vm->bind_alloc)

It is a shorter hacky way of saying:

	if (*flags & I915_VMA_LOCAL_BIND) &&
	    vma->vm->bind_allocates)

Or where you have:

	if (work && (bind_flags & ~vma_flags) & vma->vm->bind_alloc) {

This would be:

	if (work &&
	    vma->vm->bind_allocates &&
	    (bind_flags & I915_VMA_LOCAL_BIND) &&
	    !(vma_flags & I915_VMA_LOCAL_BIND)) {

But I think I see now what your code is actually saying, you are having 
vm->bind_alloc mean vm->bind_flags_which_allocate. Did I get your 
thinking right now? If so compromise with renaming to vm->bind_alloc_flags?

Regards,

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

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

* Re: [PATCH 12/21] drm/i915: Mark up address spaces that may need to allocate
  2019-09-25 15:59           ` Tvrtko Ursulin
@ 2019-09-27 17:03             ` Chris Wilson
  0 siblings, 0 replies; 53+ messages in thread
From: Chris Wilson @ 2019-09-27 17:03 UTC (permalink / raw)
  To: Tvrtko Ursulin, intel-gfx

Quoting Tvrtko Ursulin (2019-09-25 16:59:26)
> 
> On 25/09/2019 09:23, Chris Wilson wrote:
> > Quoting Tvrtko Ursulin (2019-09-23 09:10:26)
> >>
> >> On 20/09/2019 17:35, Chris Wilson wrote:
> >>> Quoting Tvrtko Ursulin (2019-09-20 17:22:42)
> >>>>
> >>>> On 02/09/2019 05:02, Chris Wilson wrote:
> >>>>> Since we cannot allocate underneath the vm->mutex (it is used in the
> >>>>> direct-reclaim paths), we need to shift the allocations off into a
> >>>>> mutexless worker with fence recursion prevention. To know when we need
> >>>>> this protection, we mark up the address spaces that do allocate before
> >>>>> insertion.
> >>>>>
> >>>>> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> >>>>> ---
> >>>>>     drivers/gpu/drm/i915/i915_gem_gtt.c | 3 +++
> >>>>>     drivers/gpu/drm/i915/i915_gem_gtt.h | 2 ++
> >>>>>     2 files changed, 5 insertions(+)
> >>>>>
> >>>>> diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
> >>>>> index 9095f017162e..56d27cf09a3d 100644
> >>>>> --- a/drivers/gpu/drm/i915/i915_gem_gtt.c
> >>>>> +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
> >>>>> @@ -1500,6 +1500,7 @@ static struct i915_ppgtt *gen8_ppgtt_create(struct drm_i915_private *i915)
> >>>>>                         goto err_free_pd;
> >>>>>         }
> >>>>>     
> >>>>> +     ppgtt->vm.bind_alloc = I915_VMA_LOCAL_BIND;
> >>>>
> >>>> So this is re-using I915_VMA_LOCAL_BIND as a trick? Is it clear how that
> >>>> works from these call sites? Should it be called bind_alloc*s*?
> >>>> bind_allocates? Or be a boolean which is converted to a trick flag in
> >>>> i915_vma_bind where a comment can be put explaining the trick?
> >>>
> >>> Is it a trick? We need to differentiate between requests for LOCAL_BIND,
> >>> GLOBAL_BIND, LOCAL_BIND | GLOBAL_BIND, for different types of vm. Then I
> >>> have a plan on using the worker for GLOBAL_BIND on bsw/bxt to defer the
> >>> stop_machine().
> >>
> >> What's the connection between "mark up the address spaces that do
> >> allocate before insertion" and I915_VMA_LOCAL_BIND?
> > 
> > Full-ppgtt is only accessible by PIN_USER.
> > 
> > Aliasing-ppgtt is accessible from global-gtt as PIN_USER. Only if we
> > have an aliasing-gtt behind ggtt do we want to allocate for ggtt for
> > local binds.
> > 
> > global-gtt by itself never allocates and is expected to be synchronous.
> > However, we do use stop_machine() for bxt/bsw and that unfortunately is
> > marked as an allocating mutex so one idea I had for avoiding that
> > lockdep splat was to make bxt/bsw PIN_GLOBAL async.
> 
> I think we are not understanding each other from the very start.
> 
> My point was that "vm.bind_alloc = I915_VMA_LOCAL_BIND", at least my 
> understanding, effectively means "use the worker when pinning/binding 
> PIN_USER/I915_VMA_LOCAL_BIND". And that is I think non-obvious. Where 
> you have in the code:
> 
>         if (flags & vma->vm->bind_alloc)
> 
> It is a shorter hacky way of saying:
> 
>         if (*flags & I915_VMA_LOCAL_BIND) &&
>             vma->vm->bind_allocates)
> 
> Or where you have:
> 
>         if (work && (bind_flags & ~vma_flags) & vma->vm->bind_alloc) {
> 
> This would be:
> 
>         if (work &&
>             vma->vm->bind_allocates &&
>             (bind_flags & I915_VMA_LOCAL_BIND) &&
>             !(vma_flags & I915_VMA_LOCAL_BIND)) {
> 
> But I think I see now what your code is actually saying, you are having 
> vm->bind_alloc mean vm->bind_flags_which_allocate. Did I get your 
> thinking right now? If so compromise with renaming to vm->bind_alloc_flags?

vm->bind_alloc_flags it is.
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* [PATCH 10/21] drm/i915: Make i915_vma.flags atomic_t for mutex reduction
  2019-08-30  6:11 [PATCH 01/21] drm/i915/gtt: Downgrade Baytrail back to aliasing-ppgtt Chris Wilson
@ 2019-08-30  6:11 ` Chris Wilson
  0 siblings, 0 replies; 53+ messages in thread
From: Chris Wilson @ 2019-08-30  6:11 UTC (permalink / raw)
  To: intel-gfx

In preparation for reducing struct_mutex stranglehold around the vm,
make the vma.flags atomic so that we can acquire a pin on the vma
atomically before deciding if we need to take the mutex.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 drivers/gpu/drm/i915/gem/i915_gem_object.c |  2 +-
 drivers/gpu/drm/i915/gem/i915_gem_stolen.c |  2 +-
 drivers/gpu/drm/i915/i915_gem_gtt.c        | 14 ++---
 drivers/gpu/drm/i915/i915_vma.c            | 29 +++++-----
 drivers/gpu/drm/i915/i915_vma.h            | 63 +++++++++++++---------
 drivers/gpu/drm/i915/selftests/mock_gtt.c  |  4 +-
 6 files changed, 65 insertions(+), 49 deletions(-)

diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.c b/drivers/gpu/drm/i915/gem/i915_gem_object.c
index d7855dc5a5c5..0ef60dae23a7 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_object.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_object.c
@@ -163,7 +163,7 @@ static void __i915_gem_free_objects(struct drm_i915_private *i915,
 
 		list_for_each_entry_safe(vma, vn, &obj->vma.list, obj_link) {
 			GEM_BUG_ON(i915_vma_is_active(vma));
-			vma->flags &= ~I915_VMA_PIN_MASK;
+			atomic_and(~I915_VMA_PIN_MASK, &vma->flags);
 			i915_vma_destroy(vma);
 		}
 		GEM_BUG_ON(!list_empty(&obj->vma.list));
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_stolen.c b/drivers/gpu/drm/i915/gem/i915_gem_stolen.c
index 2e1bfd5e4adf..0d81de1461b4 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_stolen.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_stolen.c
@@ -685,7 +685,7 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_i915_private *dev_priv
 	GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
 
 	vma->pages = obj->mm.pages;
-	vma->flags |= I915_VMA_GLOBAL_BIND;
+	set_bit(I915_VMA_GLOBAL_BIND_BIT, __i915_vma_flags(vma));
 	__i915_vma_set_map_and_fenceable(vma);
 
 	mutex_lock(&ggtt->vm.mutex);
diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
index e766127c1bd5..dbd18f81c294 100644
--- a/drivers/gpu/drm/i915/i915_gem_gtt.c
+++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
@@ -155,7 +155,7 @@ static int ppgtt_bind_vma(struct i915_vma *vma,
 	u32 pte_flags;
 	int err;
 
-	if (!(vma->flags & I915_VMA_LOCAL_BIND)) {
+	if (!i915_vma_is_bound(vma, I915_VMA_LOCAL_BIND)) {
 		err = vma->vm->allocate_va_range(vma->vm,
 						 vma->node.start, vma->size);
 		if (err)
@@ -1866,7 +1866,7 @@ static struct i915_vma *pd_vma_create(struct gen6_ppgtt *ppgtt, int size)
 
 	vma->size = size;
 	vma->fence_size = size;
-	vma->flags = I915_VMA_GGTT;
+	atomic_set(&vma->flags, I915_VMA_GGTT);
 	vma->ggtt_view.type = I915_GGTT_VIEW_ROTATED; /* prevent fencing */
 
 	INIT_LIST_HEAD(&vma->obj_link);
@@ -2425,7 +2425,7 @@ static int ggtt_bind_vma(struct i915_vma *vma,
 	 * GLOBAL/LOCAL_BIND, it's all the same ptes. Hence unconditionally
 	 * upgrade to both bound if we bind either to avoid double-binding.
 	 */
-	vma->flags |= I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND;
+	atomic_or(I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND, &vma->flags);
 
 	return 0;
 }
@@ -2455,7 +2455,7 @@ static int aliasing_gtt_bind_vma(struct i915_vma *vma,
 	if (flags & I915_VMA_LOCAL_BIND) {
 		struct i915_ppgtt *alias = i915_vm_to_ggtt(vma->vm)->alias;
 
-		if (!(vma->flags & I915_VMA_LOCAL_BIND)) {
+		if (!i915_vma_is_bound(vma, I915_VMA_LOCAL_BIND)) {
 			ret = alias->vm.allocate_va_range(&alias->vm,
 							  vma->node.start,
 							  vma->size);
@@ -2483,7 +2483,7 @@ static void aliasing_gtt_unbind_vma(struct i915_vma *vma)
 {
 	struct drm_i915_private *i915 = vma->vm->i915;
 
-	if (vma->flags & I915_VMA_GLOBAL_BIND) {
+	if (i915_vma_is_bound(vma, I915_VMA_GLOBAL_BIND)) {
 		struct i915_address_space *vm = vma->vm;
 		intel_wakeref_t wakeref;
 
@@ -2491,7 +2491,7 @@ static void aliasing_gtt_unbind_vma(struct i915_vma *vma)
 			vm->clear_range(vm, vma->node.start, vma->size);
 	}
 
-	if (vma->flags & I915_VMA_LOCAL_BIND) {
+	if (i915_vma_is_bound(vma, I915_VMA_LOCAL_BIND)) {
 		struct i915_address_space *vm =
 			&i915_vm_to_ggtt(vma->vm)->alias->vm;
 
@@ -3291,7 +3291,7 @@ static void ggtt_restore_mappings(struct i915_ggtt *ggtt)
 	list_for_each_entry_safe(vma, vn, &ggtt->vm.bound_list, vm_link) {
 		struct drm_i915_gem_object *obj = vma->obj;
 
-		if (!(vma->flags & I915_VMA_GLOBAL_BIND))
+		if (!i915_vma_is_bound(vma, I915_VMA_GLOBAL_BIND))
 			continue;
 
 		mutex_unlock(&ggtt->vm.mutex);
diff --git a/drivers/gpu/drm/i915/i915_vma.c b/drivers/gpu/drm/i915/i915_vma.c
index 848605ffff2d..9adb85ba6daa 100644
--- a/drivers/gpu/drm/i915/i915_vma.c
+++ b/drivers/gpu/drm/i915/i915_vma.c
@@ -171,7 +171,7 @@ vma_create(struct drm_i915_gem_object *obj,
 								i915_gem_object_get_stride(obj));
 		GEM_BUG_ON(!is_power_of_2(vma->fence_alignment));
 
-		vma->flags |= I915_VMA_GGTT;
+		__set_bit(I915_VMA_GGTT_BIT, __i915_vma_flags(vma));
 	}
 
 	spin_lock(&obj->vma.lock);
@@ -321,7 +321,8 @@ int i915_vma_bind(struct i915_vma *vma, enum i915_cache_level cache_level,
 	if (flags & PIN_USER)
 		bind_flags |= I915_VMA_LOCAL_BIND;
 
-	vma_flags = vma->flags & (I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND);
+	vma_flags = atomic_read(&vma->flags);
+	vma_flags &= I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND;
 	if (flags & PIN_UPDATE)
 		bind_flags |= vma_flags;
 	else
@@ -336,7 +337,7 @@ int i915_vma_bind(struct i915_vma *vma, enum i915_cache_level cache_level,
 	if (ret)
 		return ret;
 
-	vma->flags |= bind_flags;
+	atomic_or(bind_flags, &vma->flags);
 	return 0;
 }
 
@@ -355,7 +356,7 @@ void __iomem *i915_vma_pin_iomap(struct i915_vma *vma)
 	}
 
 	GEM_BUG_ON(!i915_vma_is_ggtt(vma));
-	GEM_BUG_ON((vma->flags & I915_VMA_GLOBAL_BIND) == 0);
+	GEM_BUG_ON(!i915_vma_is_bound(vma, I915_VMA_GLOBAL_BIND));
 
 	ptr = vma->iomap;
 	if (ptr == NULL) {
@@ -468,9 +469,9 @@ void __i915_vma_set_map_and_fenceable(struct i915_vma *vma)
 	mappable = vma->node.start + vma->fence_size <= i915_vm_to_ggtt(vma->vm)->mappable_end;
 
 	if (mappable && fenceable)
-		vma->flags |= I915_VMA_CAN_FENCE;
+		set_bit(I915_VMA_CAN_FENCE_BIT, __i915_vma_flags(vma));
 	else
-		vma->flags &= ~I915_VMA_CAN_FENCE;
+		clear_bit(I915_VMA_CAN_FENCE_BIT, __i915_vma_flags(vma));
 }
 
 static bool color_differs(struct drm_mm_node *node, unsigned long color)
@@ -543,7 +544,7 @@ i915_vma_insert(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
 	int ret;
 
 	GEM_BUG_ON(i915_vma_is_closed(vma));
-	GEM_BUG_ON(vma->flags & (I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND));
+	GEM_BUG_ON(i915_vma_is_bound(vma, I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND));
 	GEM_BUG_ON(drm_mm_node_allocated(&vma->node));
 
 	size = max(size, vma->size);
@@ -677,7 +678,7 @@ static void
 i915_vma_remove(struct i915_vma *vma)
 {
 	GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
-	GEM_BUG_ON(vma->flags & (I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND));
+	GEM_BUG_ON(i915_vma_is_bound(vma, I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND));
 
 	vma->ops->clear_pages(vma);
 
@@ -708,7 +709,7 @@ i915_vma_remove(struct i915_vma *vma)
 int __i915_vma_do_pin(struct i915_vma *vma,
 		      u64 size, u64 alignment, u64 flags)
 {
-	const unsigned int bound = vma->flags;
+	const unsigned int bound = atomic_read(&vma->flags);
 	int ret;
 
 	lockdep_assert_held(&vma->vm->i915->drm.struct_mutex);
@@ -731,9 +732,9 @@ int __i915_vma_do_pin(struct i915_vma *vma,
 	if (ret)
 		goto err_remove;
 
-	GEM_BUG_ON((vma->flags & I915_VMA_BIND_MASK) == 0);
+	GEM_BUG_ON(!i915_vma_is_bound(vma, I915_VMA_BIND_MASK));
 
-	if ((bound ^ vma->flags) & I915_VMA_GLOBAL_BIND)
+	if ((bound ^ atomic_read(&vma->flags)) & I915_VMA_GLOBAL_BIND)
 		__i915_vma_set_map_and_fenceable(vma);
 
 	GEM_BUG_ON(i915_vma_misplaced(vma, size, alignment, flags));
@@ -743,7 +744,7 @@ int __i915_vma_do_pin(struct i915_vma *vma,
 	if ((bound & I915_VMA_BIND_MASK) == 0) {
 		i915_vma_remove(vma);
 		GEM_BUG_ON(vma->pages);
-		GEM_BUG_ON(vma->flags & I915_VMA_BIND_MASK);
+		GEM_BUG_ON(atomic_read(&vma->flags) & I915_VMA_BIND_MASK);
 	}
 err_unpin:
 	__i915_vma_unpin(vma);
@@ -986,7 +987,7 @@ int i915_vma_unbind(struct i915_vma *vma)
 		mutex_unlock(&vma->vm->mutex);
 
 		__i915_vma_iounmap(vma);
-		vma->flags &= ~I915_VMA_CAN_FENCE;
+		clear_bit(I915_VMA_CAN_FENCE_BIT, __i915_vma_flags(vma));
 	}
 	GEM_BUG_ON(vma->fence);
 	GEM_BUG_ON(i915_vma_has_userfault(vma));
@@ -995,7 +996,7 @@ int i915_vma_unbind(struct i915_vma *vma)
 		trace_i915_vma_unbind(vma);
 		vma->ops->unbind_vma(vma);
 	}
-	vma->flags &= ~(I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND);
+	atomic_and(~I915_VMA_BIND_MASK, &vma->flags);
 
 	i915_vma_remove(vma);
 
diff --git a/drivers/gpu/drm/i915/i915_vma.h b/drivers/gpu/drm/i915/i915_vma.h
index af2ef0a51455..02d7d815407c 100644
--- a/drivers/gpu/drm/i915/i915_vma.h
+++ b/drivers/gpu/drm/i915/i915_vma.h
@@ -72,7 +72,7 @@ struct i915_vma {
 	 * that exist in the ctx->handle_vmas LUT for this vma.
 	 */
 	atomic_t open_count;
-	unsigned long flags;
+	atomic_t flags;
 	/**
 	 * How many users have pinned this object in GTT space.
 	 *
@@ -97,18 +97,29 @@ struct i915_vma {
 	 * users.
 	 */
 #define I915_VMA_PIN_MASK 0xff
-#define I915_VMA_PIN_OVERFLOW	BIT(8)
+#define I915_VMA_PIN_OVERFLOW_BIT 8
+#define I915_VMA_PIN_OVERFLOW	((int)BIT(I915_VMA_PIN_OVERFLOW_BIT))
 
 	/** Flags and address space this VMA is bound to */
-#define I915_VMA_GLOBAL_BIND	BIT(9)
-#define I915_VMA_LOCAL_BIND	BIT(10)
-#define I915_VMA_BIND_MASK (I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND | I915_VMA_PIN_OVERFLOW)
+#define I915_VMA_GLOBAL_BIND_BIT 9
+#define I915_VMA_LOCAL_BIND_BIT 10
 
-#define I915_VMA_GGTT		BIT(11)
-#define I915_VMA_CAN_FENCE	BIT(12)
+#define I915_VMA_GLOBAL_BIND	((int)BIT(I915_VMA_GLOBAL_BIND_BIT))
+#define I915_VMA_LOCAL_BIND	((int)BIT(I915_VMA_LOCAL_BIND_BIT))
+
+#define I915_VMA_BIND_MASK (I915_VMA_GLOBAL_BIND | \
+			    I915_VMA_LOCAL_BIND | \
+			    I915_VMA_PIN_OVERFLOW)
+
+#define I915_VMA_GGTT_BIT	11
+#define I915_VMA_CAN_FENCE_BIT	12
 #define I915_VMA_USERFAULT_BIT	13
-#define I915_VMA_USERFAULT	BIT(I915_VMA_USERFAULT_BIT)
-#define I915_VMA_GGTT_WRITE	BIT(14)
+#define I915_VMA_GGTT_WRITE_BIT	14
+
+#define I915_VMA_GGTT		((int)BIT(I915_VMA_GGTT_BIT))
+#define I915_VMA_CAN_FENCE	((int)BIT(I915_VMA_CAN_FENCE_BIT))
+#define I915_VMA_USERFAULT	((int)BIT(I915_VMA_USERFAULT_BIT))
+#define I915_VMA_GGTT_WRITE	((int)BIT(I915_VMA_GGTT_WRITE_BIT))
 
 	struct i915_active active;
 
@@ -162,48 +173,52 @@ int __must_check i915_vma_move_to_active(struct i915_vma *vma,
 					 struct i915_request *rq,
 					 unsigned int flags);
 
+#define __i915_vma_flags(v) ((unsigned long *)&(v)->flags)
+
 static inline bool i915_vma_is_ggtt(const struct i915_vma *vma)
 {
-	return vma->flags & I915_VMA_GGTT;
+	return test_bit(I915_VMA_GGTT_BIT, __i915_vma_flags(vma));
 }
 
 static inline bool i915_vma_has_ggtt_write(const struct i915_vma *vma)
 {
-	return vma->flags & I915_VMA_GGTT_WRITE;
+	return test_bit(I915_VMA_GGTT_WRITE_BIT, __i915_vma_flags(vma));
 }
 
 static inline void i915_vma_set_ggtt_write(struct i915_vma *vma)
 {
 	GEM_BUG_ON(!i915_vma_is_ggtt(vma));
-	vma->flags |= I915_VMA_GGTT_WRITE;
+	set_bit(I915_VMA_GGTT_WRITE_BIT, __i915_vma_flags(vma));
 }
 
-static inline void i915_vma_unset_ggtt_write(struct i915_vma *vma)
+static inline bool i915_vma_unset_ggtt_write(struct i915_vma *vma)
 {
-	vma->flags &= ~I915_VMA_GGTT_WRITE;
+	return test_and_clear_bit(I915_VMA_GGTT_WRITE_BIT,
+				  __i915_vma_flags(vma));
 }
 
 void i915_vma_flush_writes(struct i915_vma *vma);
 
 static inline bool i915_vma_is_map_and_fenceable(const struct i915_vma *vma)
 {
-	return vma->flags & I915_VMA_CAN_FENCE;
+	return test_bit(I915_VMA_CAN_FENCE_BIT, __i915_vma_flags(vma));
 }
 
 static inline bool i915_vma_set_userfault(struct i915_vma *vma)
 {
 	GEM_BUG_ON(!i915_vma_is_map_and_fenceable(vma));
-	return __test_and_set_bit(I915_VMA_USERFAULT_BIT, &vma->flags);
+	return __test_and_set_bit(I915_VMA_USERFAULT_BIT,
+				  __i915_vma_flags(vma));
 }
 
 static inline void i915_vma_unset_userfault(struct i915_vma *vma)
 {
-	return __clear_bit(I915_VMA_USERFAULT_BIT, &vma->flags);
+	return __clear_bit(I915_VMA_USERFAULT_BIT, __i915_vma_flags(vma));
 }
 
 static inline bool i915_vma_has_userfault(const struct i915_vma *vma)
 {
-	return test_bit(I915_VMA_USERFAULT_BIT, &vma->flags);
+	return test_bit(I915_VMA_USERFAULT_BIT, __i915_vma_flags(vma));
 }
 
 static inline bool i915_vma_is_closed(const struct i915_vma *vma)
@@ -330,7 +345,7 @@ i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
 	/* Pin early to prevent the shrinker/eviction logic from destroying
 	 * our vma as we insert and bind.
 	 */
-	if (likely(((++vma->flags ^ flags) & I915_VMA_BIND_MASK) == 0)) {
+	if (likely(((atomic_inc_return(&vma->flags) ^ flags) & I915_VMA_BIND_MASK) == 0)) {
 		GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
 		GEM_BUG_ON(i915_vma_misplaced(vma, size, alignment, flags));
 		return 0;
@@ -341,7 +356,7 @@ i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
 
 static inline int i915_vma_pin_count(const struct i915_vma *vma)
 {
-	return vma->flags & I915_VMA_PIN_MASK;
+	return atomic_read(&vma->flags) & I915_VMA_PIN_MASK;
 }
 
 static inline bool i915_vma_is_pinned(const struct i915_vma *vma)
@@ -351,13 +366,13 @@ static inline bool i915_vma_is_pinned(const struct i915_vma *vma)
 
 static inline void __i915_vma_pin(struct i915_vma *vma)
 {
-	vma->flags++;
-	GEM_BUG_ON(vma->flags & I915_VMA_PIN_OVERFLOW);
+	atomic_inc(&vma->flags);
+	GEM_BUG_ON(atomic_read(&vma->flags) & I915_VMA_PIN_OVERFLOW);
 }
 
 static inline void __i915_vma_unpin(struct i915_vma *vma)
 {
-	vma->flags--;
+	atomic_dec(&vma->flags);
 }
 
 static inline void i915_vma_unpin(struct i915_vma *vma)
@@ -370,7 +385,7 @@ static inline void i915_vma_unpin(struct i915_vma *vma)
 static inline bool i915_vma_is_bound(const struct i915_vma *vma,
 				     unsigned int where)
 {
-	return vma->flags & where;
+	return atomic_read(&vma->flags) & where;
 }
 
 /**
diff --git a/drivers/gpu/drm/i915/selftests/mock_gtt.c b/drivers/gpu/drm/i915/selftests/mock_gtt.c
index e62a67e0f79c..366335981086 100644
--- a/drivers/gpu/drm/i915/selftests/mock_gtt.c
+++ b/drivers/gpu/drm/i915/selftests/mock_gtt.c
@@ -43,7 +43,7 @@ static int mock_bind_ppgtt(struct i915_vma *vma,
 			   u32 flags)
 {
 	GEM_BUG_ON(flags & I915_VMA_GLOBAL_BIND);
-	vma->flags |= I915_VMA_LOCAL_BIND;
+	set_bit(I915_VMA_LOCAL_BIND_BIT, __i915_vma_flags(vma));
 	return 0;
 }
 
@@ -86,7 +86,7 @@ static int mock_bind_ggtt(struct i915_vma *vma,
 			  enum i915_cache_level cache_level,
 			  u32 flags)
 {
-	vma->flags |= I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND;
+	atomic_or(I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND, &vma->flags);
 	return 0;
 }
 
-- 
2.23.0

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

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

end of thread, other threads:[~2019-09-27 17:03 UTC | newest]

Thread overview: 53+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-09-02  4:02 [PATCH 01/21] drm/i915: Restrict the aliasing-ppgtt to the size of the ggtt Chris Wilson
2019-09-02  4:02 ` [PATCH 02/21] drm/i915: Report aliasing ppgtt size as ggtt size Chris Wilson
2019-09-02  8:55   ` Matthew Auld
2019-09-02  4:02 ` [PATCH 03/21] drm/i915/execlists: Ignore lost completion events Chris Wilson
2019-09-02  4:02 ` [PATCH 04/21] drm/i915: Refresh the errno to vmf_fault translations Chris Wilson
2019-09-03 15:34   ` Abdiel Janulgue
2019-09-02  4:02 ` [PATCH 05/21] drm/i915: Replace obj->pin_global with obj->frontbuffer Chris Wilson
2019-09-02  4:02 ` [PATCH 06/21] dma-fence: Serialise signal enabling (dma_fence_enable_sw_signaling) Chris Wilson
2019-09-02  4:02 ` [PATCH 07/21] drm/mm: Pack allocated/scanned boolean into a bitfield Chris Wilson
2019-09-02  4:02 ` [PATCH 08/21] drm/i915: Make shrink/unshrink be atomic Chris Wilson
2019-09-10 19:54   ` Matthew Auld
2019-09-02  4:02 ` [PATCH 09/21] drm/i915: Only track bound elements of the GTT Chris Wilson
2019-09-02  4:02 ` [PATCH 10/21] drm/i915: Make i915_vma.flags atomic_t for mutex reduction Chris Wilson
2019-09-10 20:06   ` Matthew Auld
2019-09-02  4:02 ` [PATCH 11/21] drm/i915/gtt: Make sure the gen6 ppgtt is bound before first use Chris Wilson
2019-09-10 20:17   ` Matthew Auld
2019-09-02  4:02 ` [PATCH 12/21] drm/i915: Mark up address spaces that may need to allocate Chris Wilson
2019-09-20 16:22   ` Tvrtko Ursulin
2019-09-20 16:35     ` Chris Wilson
2019-09-23  8:10       ` Tvrtko Ursulin
2019-09-25  8:23         ` Chris Wilson
2019-09-25 15:59           ` Tvrtko Ursulin
2019-09-27 17:03             ` Chris Wilson
2019-09-02  4:02 ` [PATCH 13/21] drm/i915: Pull i915_vma_pin under the vm->mutex Chris Wilson
2019-09-16 10:13   ` Tvrtko Ursulin
2019-09-16 11:10     ` Chris Wilson
2019-09-17 12:37   ` Tvrtko Ursulin
2019-09-17 18:56     ` Chris Wilson
2019-09-19 13:37       ` Tvrtko Ursulin
2019-09-19 14:05         ` Chris Wilson
2019-09-02  4:02 ` [PATCH 14/21] drm/i915: Push the i915_active.retire into a worker Chris Wilson
2019-09-02  4:02 ` [PATCH 15/21] drm/i915: Coordinate i915_active with its own mutex Chris Wilson
2019-09-20 16:14   ` Tvrtko Ursulin
2019-09-20 16:32     ` Chris Wilson
2019-09-02  4:02 ` [PATCH 16/21] drm/i915: Move idle barrier cleanup into engine-pm Chris Wilson
2019-09-20 16:18   ` Tvrtko Ursulin
2019-09-02  4:02 ` [PATCH 17/21] drm/i915: Drop struct_mutex from around i915_retire_requests() Chris Wilson
2019-09-24 15:25   ` Tvrtko Ursulin
2019-09-25  8:43     ` Chris Wilson
2019-09-25  8:49       ` Tvrtko Ursulin
2019-09-02  4:03 ` [PATCH 18/21] drm/i915: Remove the GEM idle worker Chris Wilson
2019-09-24 15:26   ` Tvrtko Ursulin
2019-09-02  4:03 ` [PATCH 19/21] drm/i915: Merge wait_for_timelines with retire_request Chris Wilson
2019-09-24 15:57   ` Tvrtko Ursulin
2019-09-25  8:54     ` Chris Wilson
2019-09-02  4:03 ` [PATCH 20/21] drm/i915: Move request runtime management onto gt Chris Wilson
2019-09-02  4:03 ` [PATCH 21/21] drm/i915: Move global activity tracking from GEM to GT Chris Wilson
2019-09-25  9:55   ` Tvrtko Ursulin
2019-09-02  4:54 ` ✗ Fi.CI.CHECKPATCH: warning for series starting with [01/21] drm/i915: Restrict the aliasing-ppgtt to the size of the ggtt Patchwork
2019-09-02  5:20 ` ✓ Fi.CI.BAT: success " Patchwork
2019-09-02  8:00 ` ✗ Fi.CI.IGT: failure " Patchwork
2019-09-02  8:52 ` [PATCH 01/21] " Matthew Auld
  -- strict thread matches above, loose matches on Subject: below --
2019-08-30  6:11 [PATCH 01/21] drm/i915/gtt: Downgrade Baytrail back to aliasing-ppgtt Chris Wilson
2019-08-30  6:11 ` [PATCH 10/21] drm/i915: Make i915_vma.flags atomic_t for mutex reduction Chris Wilson

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.