[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 17:15:53 UTC 2016
On Mon, Aug 22, 2016 at 05:56:24PM +0100, Chris Wilson wrote:
> 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;
Hmm, prepare_to_wait() itself has a might_sleep() check, preventing the
preempt fun. The challenge is that we do not want to be interrupted
once we decide to apply the update (reading the scanline) - but that
read has to be after prepare_to_wait to prevent any races before
sleeping.
-Chris
--
Chris Wilson, Intel Open Source Technology Centre
More information about the Intel-gfx
mailing list