[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