aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/i915/intel_display.c
diff options
context:
space:
mode:
authorChris Wilson <chris@chris-wilson.co.uk>2012-12-03 06:36:30 -0500
committerDaniel Vetter <daniel.vetter@ffwll.ch>2012-12-06 08:09:37 -0500
commite7d841ca03b7ab668620045cd7b428eda9f41601 (patch)
tree513a8adda0a306e3e8c4b10d27f31992ed28f6b6 /drivers/gpu/drm/i915/intel_display.c
parentebf69cb8331d7336e4bcd442a2ca69bb61739a58 (diff)
drm/i915: Close race between processing unpin task and queueing the flip
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@chris-wilson.co.uk> Cc: stable@vger.kernel.org [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@ffwll.ch>
Diffstat (limited to 'drivers/gpu/drm/i915/intel_display.c')
-rw-r--r--drivers/gpu/drm/i915/intel_display.c39
1 files changed, 32 insertions, 7 deletions
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 78d12c471472..f8ee3d16109c 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -6929,11 +6929,18 @@ static void do_intel_finish_page_flip(struct drm_device *dev,
6929 6929
6930 spin_lock_irqsave(&dev->event_lock, flags); 6930 spin_lock_irqsave(&dev->event_lock, flags);
6931 work = intel_crtc->unpin_work; 6931 work = intel_crtc->unpin_work;
6932 if (work == NULL || !work->pending) { 6932
6933 /* Ensure we don't miss a work->pending update ... */
6934 smp_rmb();
6935
6936 if (work == NULL || atomic_read(&work->pending) < INTEL_FLIP_COMPLETE) {
6933 spin_unlock_irqrestore(&dev->event_lock, flags); 6937 spin_unlock_irqrestore(&dev->event_lock, flags);
6934 return; 6938 return;
6935 } 6939 }
6936 6940
6941 /* and that the unpin work is consistent wrt ->pending. */
6942 smp_rmb();
6943
6937 intel_crtc->unpin_work = NULL; 6944 intel_crtc->unpin_work = NULL;
6938 6945
6939 if (work->event) 6946 if (work->event)
@@ -6977,16 +6984,25 @@ void intel_prepare_page_flip(struct drm_device *dev, int plane)
6977 to_intel_crtc(dev_priv->plane_to_crtc_mapping[plane]); 6984 to_intel_crtc(dev_priv->plane_to_crtc_mapping[plane]);
6978 unsigned long flags; 6985 unsigned long flags;
6979 6986
6987 /* NB: An MMIO update of the plane base pointer will also
6988 * generate a page-flip completion irq, i.e. every modeset
6989 * is also accompanied by a spurious intel_prepare_page_flip().
6990 */
6980 spin_lock_irqsave(&dev->event_lock, flags); 6991 spin_lock_irqsave(&dev->event_lock, flags);
6981 if (intel_crtc->unpin_work) { 6992 if (intel_crtc->unpin_work)
6982 if ((++intel_crtc->unpin_work->pending) > 1) 6993 atomic_inc_not_zero(&intel_crtc->unpin_work->pending);
6983 DRM_ERROR("Prepared flip multiple times\n");
6984 } else {
6985 DRM_DEBUG_DRIVER("preparing flip with no unpin work?\n");
6986 }
6987 spin_unlock_irqrestore(&dev->event_lock, flags); 6994 spin_unlock_irqrestore(&dev->event_lock, flags);
6988} 6995}
6989 6996
6997inline static void intel_mark_page_flip_active(struct intel_crtc *intel_crtc)
6998{
6999 /* Ensure that the work item is consistent when activating it ... */
7000 smp_wmb();
7001 atomic_set(&intel_crtc->unpin_work->pending, INTEL_FLIP_PENDING);
7002 /* and that it is marked active as soon as the irq could fire. */
7003 smp_wmb();
7004}
7005
6990static int intel_gen2_queue_flip(struct drm_device *dev, 7006static int intel_gen2_queue_flip(struct drm_device *dev,
6991 struct drm_crtc *crtc, 7007 struct drm_crtc *crtc,
6992 struct drm_framebuffer *fb, 7008 struct drm_framebuffer *fb,
@@ -7020,6 +7036,8 @@ static int intel_gen2_queue_flip(struct drm_device *dev,
7020 intel_ring_emit(ring, fb->pitches[0]); 7036 intel_ring_emit(ring, fb->pitches[0]);
7021 intel_ring_emit(ring, obj->gtt_offset + intel_crtc->dspaddr_offset); 7037 intel_ring_emit(ring, obj->gtt_offset + intel_crtc->dspaddr_offset);
7022 intel_ring_emit(ring, 0); /* aux display base address, unused */ 7038 intel_ring_emit(ring, 0); /* aux display base address, unused */
7039
7040 intel_mark_page_flip_active(intel_crtc);
7023 intel_ring_advance(ring); 7041 intel_ring_advance(ring);
7024 return 0; 7042 return 0;
7025 7043
@@ -7060,6 +7078,7 @@ static int intel_gen3_queue_flip(struct drm_device *dev,
7060 intel_ring_emit(ring, obj->gtt_offset + intel_crtc->dspaddr_offset); 7078 intel_ring_emit(ring, obj->gtt_offset + intel_crtc->dspaddr_offset);
7061 intel_ring_emit(ring, MI_NOOP); 7079 intel_ring_emit(ring, MI_NOOP);
7062 7080
7081 intel_mark_page_flip_active(intel_crtc);
7063 intel_ring_advance(ring); 7082 intel_ring_advance(ring);
7064 return 0; 7083 return 0;
7065 7084
@@ -7106,6 +7125,8 @@ static int intel_gen4_queue_flip(struct drm_device *dev,
7106 pf = 0; 7125 pf = 0;
7107 pipesrc = I915_READ(PIPESRC(intel_crtc->pipe)) & 0x0fff0fff; 7126 pipesrc = I915_READ(PIPESRC(intel_crtc->pipe)) & 0x0fff0fff;
7108 intel_ring_emit(ring, pf | pipesrc); 7127 intel_ring_emit(ring, pf | pipesrc);
7128
7129 intel_mark_page_flip_active(intel_crtc);
7109 intel_ring_advance(ring); 7130 intel_ring_advance(ring);
7110 return 0; 7131 return 0;
7111 7132
@@ -7148,6 +7169,8 @@ static int intel_gen6_queue_flip(struct drm_device *dev,
7148 pf = 0; 7169 pf = 0;
7149 pipesrc = I915_READ(PIPESRC(intel_crtc->pipe)) & 0x0fff0fff; 7170 pipesrc = I915_READ(PIPESRC(intel_crtc->pipe)) & 0x0fff0fff;
7150 intel_ring_emit(ring, pf | pipesrc); 7171 intel_ring_emit(ring, pf | pipesrc);
7172
7173 intel_mark_page_flip_active(intel_crtc);
7151 intel_ring_advance(ring); 7174 intel_ring_advance(ring);
7152 return 0; 7175 return 0;
7153 7176
@@ -7202,6 +7225,8 @@ static int intel_gen7_queue_flip(struct drm_device *dev,
7202 intel_ring_emit(ring, (fb->pitches[0] | obj->tiling_mode)); 7225 intel_ring_emit(ring, (fb->pitches[0] | obj->tiling_mode));
7203 intel_ring_emit(ring, obj->gtt_offset + intel_crtc->dspaddr_offset); 7226 intel_ring_emit(ring, obj->gtt_offset + intel_crtc->dspaddr_offset);
7204 intel_ring_emit(ring, (MI_NOOP)); 7227 intel_ring_emit(ring, (MI_NOOP));
7228
7229 intel_mark_page_flip_active(intel_crtc);
7205 intel_ring_advance(ring); 7230 intel_ring_advance(ring);
7206 return 0; 7231 return 0;
7207 7232