[Intel-gfx] [PATCH] drm/i915: Close race between processing unpin task and queueing the flip
Daniel Vetter
daniel at ffwll.ch
Sat Dec 1 21:35:21 CET 2012
On Sat, Dec 01, 2012 at 05:48:50PM +0000, Chris Wilson wrote:
> 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.
>
> Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
Hm, can't this logic race?
- emit the MI_FLIP
- flip irq happens because the gpu is idle and completes it right away
(or our thread is preempted), work->pending increments from 0 -> 1
- queue_flip sets work->pending to 1
So work->pending will be stuck at 1 forverer, the unpin never happens and
stalls all subsequent flips.
Then there's also the usual annoying maintainer questions:
- do we have a way to readily reproduce this race?
- do we have a chance to just shut out all these spurious pageflip
interrupts? If they all come from MMIO access, we should be able to lock
them out somehow ...
Cheers, Daniel
> ---
> drivers/gpu/drm/i915/i915_debugfs.c | 4 ++--
> drivers/gpu/drm/i915/i915_irq.c | 4 +++-
> drivers/gpu/drm/i915/intel_display.c | 23 ++++++++++++++++-------
> drivers/gpu/drm/i915/intel_drv.h | 5 ++++-
> 4 files changed, 25 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
> index 8afc0dd..e6a11ca 100644
> --- a/drivers/gpu/drm/i915/i915_debugfs.c
> +++ b/drivers/gpu/drm/i915/i915_debugfs.c
> @@ -317,7 +317,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 {
> @@ -328,7 +328,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 6cd3dc9..a4dc97f 100644
> --- a/drivers/gpu/drm/i915/i915_irq.c
> +++ b/drivers/gpu/drm/i915/i915_irq.c
> @@ -1466,7 +1466,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 78d12c4..2746b39 100644
> --- a/drivers/gpu/drm/i915/intel_display.c
> +++ b/drivers/gpu/drm/i915/intel_display.c
> @@ -6929,7 +6929,7 @@ 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) {
> + if (work == NULL || atomic_read(&work->pending) < INTEL_FLIP_COMPLETE) {
> spin_unlock_irqrestore(&dev->event_lock, flags);
> return;
> }
> @@ -6977,13 +6977,13 @@ 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);
> }
>
> @@ -7020,6 +7020,8 @@ static int intel_gen2_queue_flip(struct drm_device *dev,
> intel_ring_emit(ring, fb->pitches[0]);
> intel_ring_emit(ring, obj->gtt_offset + intel_crtc->dspaddr_offset);
> intel_ring_emit(ring, 0); /* aux display base address, unused */
> +
> + atomic_set(&intel_crtc->unpin_work->pending, INTEL_FLIP_PENDING);
> intel_ring_advance(ring);
> return 0;
>
> @@ -7060,6 +7062,7 @@ static int intel_gen3_queue_flip(struct drm_device *dev,
> intel_ring_emit(ring, obj->gtt_offset + intel_crtc->dspaddr_offset);
> intel_ring_emit(ring, MI_NOOP);
>
> + atomic_set(&intel_crtc->unpin_work->pending, INTEL_FLIP_PENDING);
> intel_ring_advance(ring);
> return 0;
>
> @@ -7106,6 +7109,8 @@ static int intel_gen4_queue_flip(struct drm_device *dev,
> pf = 0;
> pipesrc = I915_READ(PIPESRC(intel_crtc->pipe)) & 0x0fff0fff;
> intel_ring_emit(ring, pf | pipesrc);
> +
> + atomic_set(&intel_crtc->unpin_work->pending, INTEL_FLIP_PENDING);
> intel_ring_advance(ring);
> return 0;
>
> @@ -7148,6 +7153,8 @@ static int intel_gen6_queue_flip(struct drm_device *dev,
> pf = 0;
> pipesrc = I915_READ(PIPESRC(intel_crtc->pipe)) & 0x0fff0fff;
> intel_ring_emit(ring, pf | pipesrc);
> +
> + atomic_set(&intel_crtc->unpin_work->pending, INTEL_FLIP_PENDING);
> intel_ring_advance(ring);
> return 0;
>
> @@ -7202,6 +7209,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_crtc->dspaddr_offset);
> intel_ring_emit(ring, (MI_NOOP));
> +
> + atomic_set(&intel_crtc->unpin_work->pending, INTEL_FLIP_PENDING);
> 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 522061c..3915ca9 100644
> --- a/drivers/gpu/drm/i915/intel_drv.h
> +++ b/drivers/gpu/drm/i915/intel_drv.h
> @@ -401,7 +401,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;
> };
>
> --
> 1.7.10.4
>
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/intel-gfx
--
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch
More information about the Intel-gfx
mailing list