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: sabercrom...@chromium.org
Reviewed-by: marc...@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

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

Reply via email to