[Intel-gfx] [PATCH] drm/i915/sprite: Fix race of vblank irq vs wait in vblank evasion

Chris Wilson chris at chris-wilson.co.uk
Mon Aug 22 16:56:24 UTC 2016


When we check whether we are in the danger region before a vblank at the
start of a pipe update, the irq must be enabled. This is so that as
decide whether or not to sleep there is no race between us and the irq
delivery - i.e. we want to immediately wake up if the irq arrives
before we try to sleep. Whilst here also remove the DRM_ERROR() for
hitting a jiffie count of 0 as that also has a race against the timer
irq - instead replace it with a simple check if the sleep was for more
than a jiffie (at high resolution >1ms, at low resolution >10ms) as we
know we only try to wait for the irq within 100us of the vblank.

We replace the early irq_disable for the pipe update critical section
with a preempt disable to hog the cpu, disabling irq later when we
go to program the pipe and queue the following vblank event.

Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
---
 drivers/gpu/drm/i915/intel_sprite.c | 47 ++++++++++++++++++-------------------
 1 file changed, 23 insertions(+), 24 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c
index 11f3b38229dd..a6a51f16ae0b 100644
--- a/drivers/gpu/drm/i915/intel_sprite.c
+++ b/drivers/gpu/drm/i915/intel_sprite.c
@@ -82,10 +82,9 @@ int intel_usecs_to_scanlines(const struct drm_display_mode *adjusted_mode,
 void intel_pipe_update_start(struct intel_crtc *crtc)
 {
 	const struct drm_display_mode *adjusted_mode = &crtc->config->base.adjusted_mode;
-	long timeout = msecs_to_jiffies_timeout(1);
 	int scanline, min, max, vblank_start;
-	wait_queue_head_t *wq = drm_crtc_vblank_waitqueue(&crtc->base);
-	DEFINE_WAIT(wait);
+
+	preempt_disable();
 
 	vblank_start = adjusted_mode->crtc_vblank_start;
 	if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE)
@@ -95,19 +94,21 @@ void intel_pipe_update_start(struct intel_crtc *crtc)
 	min = vblank_start - intel_usecs_to_scanlines(adjusted_mode, 100);
 	max = vblank_start - 1;
 
-	local_irq_disable();
+	crtc->debug.min_vbl = min;
+	crtc->debug.max_vbl = max;
+
+	trace_i915_pipe_update_start(crtc);
 
 	if (min <= 0 || max <= 0)
-		return;
+		goto out;
 
 	if (WARN_ON(drm_crtc_vblank_get(&crtc->base)))
-		return;
+		goto out;
 
-	crtc->debug.min_vbl = min;
-	crtc->debug.max_vbl = max;
-	trace_i915_pipe_update_start(crtc);
+	do {
+		wait_queue_head_t *wq = drm_crtc_vblank_waitqueue(&crtc->base);
+		DEFINE_WAIT(wait);
 
-	for (;;) {
 		/*
 		 * prepare_to_wait() has a memory barrier, which guarantees
 		 * other CPUs can see the task state update by the time we
@@ -119,26 +120,22 @@ void intel_pipe_update_start(struct intel_crtc *crtc)
 		if (scanline < min || scanline > max)
 			break;
 
-		if (timeout <= 0) {
-			DRM_ERROR("Potential atomic update failure on pipe %c\n",
-				  pipe_name(crtc->pipe));
-			break;
-		}
-
-		local_irq_enable();
-
-		timeout = schedule_timeout(timeout);
+		preempt_enable();
+		if (!schedule_timeout(2))
+			DRM_ERROR("vblank wait timed out\n");
+		preempt_disable();
 
-		local_irq_disable();
-	}
-
-	finish_wait(wq, &wait);
+		finish_wait(wq, &wait);
+	} while (0);
 
 	drm_crtc_vblank_put(&crtc->base);
 
-	crtc->debug.scanline_start = scanline;
+out:
+	local_irq_disable();
+
 	crtc->debug.start_vbl_time = ktime_get();
 	crtc->debug.start_vbl_count = intel_crtc_get_vblank_counter(crtc);
+	crtc->debug.scanline_start = intel_get_crtc_scanline(crtc);
 
 	trace_i915_pipe_update_vblank_evaded(crtc);
 }
@@ -192,6 +189,8 @@ void intel_pipe_update_end(struct intel_crtc *crtc, struct intel_flip_work *work
 			  crtc->debug.min_vbl, crtc->debug.max_vbl,
 			  crtc->debug.scanline_start, scanline_end);
 	}
+
+	preempt_enable();
 }
 
 static void
-- 
2.9.3



More information about the Intel-gfx mailing list