Re: [PATCH 3.4.x v4 1/1] drm/i915: Close race between processing unpin task and queueing the flip

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



please ignore the version number in title which is for our internal review.
I just forget to remove it.

Weng Meiling
Thanks!

On 2014/4/20 15:59, Weng Meiling wrote:
> From: Chris Wilson <chris@xxxxxxxxxxxxxxxxxx>
> 
> commit e7d841ca03b7ab668620045cd7b428eda9f41601 upstream.
> 
> Before queuing the flip but crucially after attaching the unpin-work to
> the crtc, we continue to setup the unpin-work. However, should the
> hardware fire early, we see the connected unpin-work and queue the task.
> The task then promptly runs and unpins the fb before we finish taking
> the required references or even pinning it... Havoc.
> 
> To close the race, we use the flip-pending atomic to indicate when the
> flip is finally setup and enqueued. So during the flip-done processing,
> we can check more accurately whether the flip was expected.
> 
> v2: Add the appropriate mb() to ensure that the writes to the page-flip
> worker are complete prior to marking it active and emitting the MI_FLIP.
> On the read side, the mb should be enforced by the spinlocks.
> 
> Signed-off-by: Chris Wilson <chris@xxxxxxxxxxxxxxxxxx>
> Cc: stable@xxxxxxxxxxxxxxx
> [danvet: Review the barriers a bit, we need a write barrier both
> before and after updating ->pending. Similarly we need a read barrier
> in the interrupt handler both before and after reading ->pending. With
> well-ordered irqs only one barrier in each place should be required,
> but since this patch explicitly sets out to combat spurious interrupts
> with is staged activation of the unpin work we need to go full-bore on
> the barriers, too. Discussed with Chris Wilson on irc and changes
> acked by him.]
> Signed-off-by: Daniel Vetter <daniel.vetter@xxxxxxxx>
> [wml: Backported to 3.4: adjust context]
> Signed-off-by: Weng Meiling <wengmeiling.weng@xxxxxxxxxx>
> ---
>  drivers/gpu/drm/i915/i915_debugfs.c  |  4 ++--
>  drivers/gpu/drm/i915/i915_irq.c      |  4 +++-
>  drivers/gpu/drm/i915/intel_display.c | 39 +++++++++++++++++++++++++++++-------
>  drivers/gpu/drm/i915/intel_drv.h     |  5 ++++-
>  4 files changed, 41 insertions(+), 11 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
> index 34791fb..bc2d0ec 100644
> --- a/drivers/gpu/drm/i915/i915_debugfs.c
> +++ b/drivers/gpu/drm/i915/i915_debugfs.c
> @@ -340,7 +340,7 @@ static int i915_gem_pageflip_info(struct seq_file *m, void *data)
>  			seq_printf(m, "No flip due on pipe %c (plane %c)\n",
>  				   pipe, plane);
>  		} else {
> -			if (!work->pending) {
> +			if (atomic_read(&work->pending) < INTEL_FLIP_COMPLETE) {
>  				seq_printf(m, "Flip queued on pipe %c (plane %c)\n",
>  					   pipe, plane);
>  			} else {
> @@ -351,7 +351,7 @@ static int i915_gem_pageflip_info(struct seq_file *m, void *data)
>  				seq_printf(m, "Stall check enabled, ");
>  			else
>  				seq_printf(m, "Stall check waiting for page flip ioctl, ");
> -			seq_printf(m, "%d prepares\n", work->pending);
> +			seq_printf(m, "%d prepares\n", atomic_read(&work->pending));
> 
>  			if (work->old_fb_obj) {
>  				struct drm_i915_gem_object *obj = work->old_fb_obj;
> diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
> index 8bca2d2..fc6f32a 100644
> --- a/drivers/gpu/drm/i915/i915_irq.c
> +++ b/drivers/gpu/drm/i915/i915_irq.c
> @@ -1251,7 +1251,9 @@ static void i915_pageflip_stall_check(struct drm_device *dev, int pipe)
>  	spin_lock_irqsave(&dev->event_lock, flags);
>  	work = intel_crtc->unpin_work;
> 
> -	if (work == NULL || work->pending || !work->enable_stall_check) {
> +	if (work == NULL ||
> +	    atomic_read(&work->pending) >= INTEL_FLIP_COMPLETE ||
> +	    !work->enable_stall_check) {
>  		/* Either the pending flip IRQ arrived, or we're too early. Don't check */
>  		spin_unlock_irqrestore(&dev->event_lock, flags);
>  		return;
> diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
> index 5647ce4..a0e80d3 100644
> --- a/drivers/gpu/drm/i915/intel_display.c
> +++ b/drivers/gpu/drm/i915/intel_display.c
> @@ -7245,11 +7245,18 @@ static void do_intel_finish_page_flip(struct drm_device *dev,
> 
>  	spin_lock_irqsave(&dev->event_lock, flags);
>  	work = intel_crtc->unpin_work;
> -	if (work == NULL || !work->pending) {
> +
> +	/* Ensure we don't miss a work->pending update ... */
> +	smp_rmb();
> +
> +	if (work == NULL || atomic_read(&work->pending) < INTEL_FLIP_COMPLETE) {
>  		spin_unlock_irqrestore(&dev->event_lock, flags);
>  		return;
>  	}
> 
> +	/* and that the unpin work is consistent wrt ->pending. */
> +	smp_rmb();
> +
>  	intel_crtc->unpin_work = NULL;
> 
>  	if (work->event) {
> @@ -7321,16 +7328,25 @@ void intel_prepare_page_flip(struct drm_device *dev, int plane)
>  		to_intel_crtc(dev_priv->plane_to_crtc_mapping[plane]);
>  	unsigned long flags;
> 
> +	/* NB: An MMIO update of the plane base pointer will also
> +	 * generate a page-flip completion irq, i.e. every modeset
> +	 * is also accompanied by a spurious intel_prepare_page_flip().
> +	 */
>  	spin_lock_irqsave(&dev->event_lock, flags);
> -	if (intel_crtc->unpin_work) {
> -		if ((++intel_crtc->unpin_work->pending) > 1)
> -			DRM_ERROR("Prepared flip multiple times\n");
> -	} else {
> -		DRM_DEBUG_DRIVER("preparing flip with no unpin work?\n");
> -	}
> +	if (intel_crtc->unpin_work)
> +		atomic_inc_not_zero(&intel_crtc->unpin_work->pending);
>  	spin_unlock_irqrestore(&dev->event_lock, flags);
>  }
> 
> +inline static void intel_mark_page_flip_active(struct intel_crtc *intel_crtc)
> +{
> +	/* Ensure that the work item is consistent when activating it ... */
> +	smp_wmb();
> +	atomic_set(&intel_crtc->unpin_work->pending, INTEL_FLIP_PENDING);
> +	/* and that it is marked active as soon as the irq could fire. */
> +	smp_wmb();
> +}
> +
>  static int intel_gen2_queue_flip(struct drm_device *dev,
>  				 struct drm_crtc *crtc,
>  				 struct drm_framebuffer *fb,
> @@ -7367,6 +7383,8 @@ static int intel_gen2_queue_flip(struct drm_device *dev,
>  	OUT_RING(fb->pitches[0]);
>  	OUT_RING(obj->gtt_offset + offset);
>  	OUT_RING(0); /* aux display base address, unused */
> +
> +	intel_mark_page_flip_active(intel_crtc);
>  	ADVANCE_LP_RING();
>  	return 0;
> 
> @@ -7410,6 +7428,7 @@ static int intel_gen3_queue_flip(struct drm_device *dev,
>  	OUT_RING(obj->gtt_offset + offset);
>  	OUT_RING(MI_NOOP);
> 
> +	intel_mark_page_flip_active(intel_crtc);
>  	ADVANCE_LP_RING();
>  	return 0;
> 
> @@ -7453,6 +7472,8 @@ static int intel_gen4_queue_flip(struct drm_device *dev,
>  	pf = 0;
>  	pipesrc = I915_READ(PIPESRC(intel_crtc->pipe)) & 0x0fff0fff;
>  	OUT_RING(pf | pipesrc);
> +
> +	intel_mark_page_flip_active(intel_crtc);
>  	ADVANCE_LP_RING();
>  	return 0;
> 
> @@ -7494,6 +7515,8 @@ static int intel_gen6_queue_flip(struct drm_device *dev,
>  	pf = 0;
>  	pipesrc = I915_READ(PIPESRC(intel_crtc->pipe)) & 0x0fff0fff;
>  	OUT_RING(pf | pipesrc);
> +
> +	intel_mark_page_flip_active(intel_crtc);
>  	ADVANCE_LP_RING();
>  	return 0;
> 
> @@ -7548,6 +7571,8 @@ static int intel_gen7_queue_flip(struct drm_device *dev,
>  	intel_ring_emit(ring, (fb->pitches[0] | obj->tiling_mode));
>  	intel_ring_emit(ring, (obj->gtt_offset));
>  	intel_ring_emit(ring, (MI_NOOP));
> +
> +	intel_mark_page_flip_active(intel_crtc);
>  	intel_ring_advance(ring);
>  	return 0;
> 
> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
> index cd623e8..018dfbd 100644
> --- a/drivers/gpu/drm/i915/intel_drv.h
> +++ b/drivers/gpu/drm/i915/intel_drv.h
> @@ -277,7 +277,10 @@ struct intel_unpin_work {
>  	struct drm_i915_gem_object *old_fb_obj;
>  	struct drm_i915_gem_object *pending_flip_obj;
>  	struct drm_pending_vblank_event *event;
> -	int pending;
> +	atomic_t pending;
> +#define INTEL_FLIP_INACTIVE	0
> +#define INTEL_FLIP_PENDING	1
> +#define INTEL_FLIP_COMPLETE	2
>  	bool enable_stall_check;
>  };
> 


--
To unsubscribe from this list: send the line "unsubscribe stable" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Kernel]     [Kernel Development Newbies]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite Hiking]     [Linux Kernel]     [Linux SCSI]