[Intel-gfx] [PATCH] Avoid i915 flip unpin/HPD event handler deadlock.

Stuart Abercrombie sabercrombie at chromium.org
Sat Aug 31 03:30:55 CEST 2013


Both of these were taking the mode_config mutex but executed from the
same work queue.  If intel_crtc_page_flip happened to flush a work queue
containing an HPD event handler work item, deadlock resulted, since the
mutex required by the HPD handler was taken before the flush.  Instead
use a separate work queue for the flip unpin work.

Signed-off-by: sabercrombie at chromium.org
Reviewed-by: marcheu at chromium.org

---
 drivers/gpu/drm/i915/i915_dma.c      | 21 ++++++++++++++++++++-
 drivers/gpu/drm/i915/i915_drv.h      |  1 +
 drivers/gpu/drm/i915/intel_display.c |  4 ++--
 3 files changed, 23 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index 4f129bb..9215360 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -1558,6 +1558,22 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
 		goto out_mtrrfree;
 	}
 
+	/* intel_crtc_page_flip runs with the mode_config mutex having been
+	 * taken in the DRM layer.  It synchronously waits for pending unpin
+	 * work items while holding this mutex.  Therefore this queue cannot
+	 * contain work items that take this mutex, such as HPD event
+	 * handling, or we deadlock.  There is also no reason for flipping to
+	 * wait on such events.  Therefore put flip unpinning in its own
+	 * work queue.
+	 */
+	dev_priv->flip_unpin_wq = alloc_ordered_workqueue("i915", 0);
+	if (dev_priv->flip_unpin_wq == NULL) {
+		DRM_ERROR("Failed to create flip unpin workqueue.\n");
+		destroy_workqueue(dev_priv->wq);
+		ret = -ENOMEM;
+		goto out_mtrrfree;
+	}
+
 	/* This must be called before any calls to HAS_PCH_* */
 	intel_detect_pch(dev);
 
@@ -1628,6 +1644,7 @@ out_gem_unload:
 	intel_teardown_gmbus(dev);
 	intel_teardown_mchbar(dev);
 	destroy_workqueue(dev_priv->wq);
+	destroy_workqueue(dev_priv->flip_unpin_wq);
 out_mtrrfree:
 	if (dev_priv->mm.gtt_mtrr >= 0) {
 		mtrr_del(dev_priv->mm.gtt_mtrr,
@@ -1709,7 +1726,8 @@ int i915_driver_unload(struct drm_device *dev)
 	intel_opregion_fini(dev);
 
 	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
-		/* Flush any outstanding unpin_work. */
+		/* Flush any outstanding unpin, HPD, etc. work. */
+		flush_workqueue(dev_priv->flip_unpin_wq);
 		flush_workqueue(dev_priv->wq);
 
 		mutex_lock(&dev->struct_mutex);
@@ -1734,6 +1752,7 @@ int i915_driver_unload(struct drm_device *dev)
 	intel_teardown_mchbar(dev);
 
 	destroy_workqueue(dev_priv->wq);
+	destroy_workqueue(dev_priv->flip_unpin_wq);
 
 	pci_dev_put(dev_priv->bridge_dev);
 	kfree(dev->dev_private);
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 4130e6d..7eb4619 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -766,6 +766,7 @@ typedef struct drm_i915_private {
 	struct work_struct error_work;
 	struct completion error_completion;
 	struct workqueue_struct *wq;
+	struct workqueue_struct *flip_unpin_wq;
 
 	/* Display functions */
 	struct drm_i915_display_funcs display;
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index e204913..acd07dd 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -6937,7 +6937,7 @@ static void do_intel_finish_page_flip(struct drm_device *dev,
 			  &obj->pending_flip.counter);
 	wake_up(&dev_priv->pending_flip_queue);
 
-	queue_work(dev_priv->wq, &work->work);
+	queue_work(dev_priv->flip_unpin_wq, &work->work);
 
 	trace_i915_flip_complete(intel_crtc->plane, work->pending_flip_obj);
 }
@@ -7305,7 +7305,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
 	spin_unlock_irqrestore(&dev->event_lock, flags);
 
 	if (atomic_read(&intel_crtc->unpin_work_count) >= 2)
-		flush_workqueue(dev_priv->wq);
+		flush_workqueue(dev_priv->flip_unpin_wq);
 
 	ret = i915_mutex_lock_interruptible(dev);
 	if (ret)
-- 
1.8.4




More information about the Intel-gfx mailing list