aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVille Syrjälä <ville.syrjala@linux.intel.com>2014-08-06 07:49:51 -0400
committerDaniel Vetter <daniel.vetter@ffwll.ch>2014-08-06 16:39:26 -0400
commit56cc279b29c7b204fe7d0943509ae209b8b128db (patch)
tree021e1b9d6044ccd573f8307236d0ade036f89a01
parent8a51d5bef07f1c8c59de20089fb27ea39d395f1b (diff)
drm: Fix deadlock between event_lock and vbl_lock/vblank_time_lock
Currently both drm_irq.c and several drivers call drm_vblank_put() while holding event_lock. Now that drm_vblank_put() can disable the vblank interrupt directly it may need to grab vbl_lock and vblank_time_lock. That causes deadlocks since we take the locks in the opposite order in two places in drm_irq.c. So let's make sure the locking order is always event_lock->vbl_lock->vblank_time_lock. In drm_vblank_off() pull up event_lock from underneath vbl_lock. Hold the event_lock across the whole operation to make sure we only send out the events that were on the queue when we disabled the interrupt, and not ones that got added just after (assuming drm_vblank_on() already managed to get called somewhere between). To sort the other deadlock pull the event_lock out from drm_handle_vblank_events() into drm_handle_vblank() to be taken outside vblank_time_lock. Add the appropriate assert_spin_locked() to drm_handle_vblank_events(). Reviewed-by: Matt Roper <matthew.d.roper@intel.com> Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
-rw-r--r--drivers/gpu/drm/drm_irq.c47
1 files changed, 25 insertions, 22 deletions
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
index b4460bf0e0e4..9353609c6770 100644
--- a/drivers/gpu/drm/drm_irq.c
+++ b/drivers/gpu/drm/drm_irq.c
@@ -1037,14 +1037,25 @@ void drm_vblank_off(struct drm_device *dev, int crtc)
1037 unsigned long irqflags; 1037 unsigned long irqflags;
1038 unsigned int seq; 1038 unsigned int seq;
1039 1039
1040 spin_lock_irqsave(&dev->vbl_lock, irqflags); 1040 spin_lock_irqsave(&dev->event_lock, irqflags);
1041
1042 spin_lock(&dev->vbl_lock);
1041 vblank_disable_and_save(dev, crtc); 1043 vblank_disable_and_save(dev, crtc);
1042 wake_up(&vblank->queue); 1044 wake_up(&vblank->queue);
1043 1045
1046 /*
1047 * Prevent subsequent drm_vblank_get() from re-enabling
1048 * the vblank interrupt by bumping the refcount.
1049 */
1050 if (!vblank->inmodeset) {
1051 atomic_inc(&vblank->refcount);
1052 vblank->inmodeset = 1;
1053 }
1054 spin_unlock(&dev->vbl_lock);
1055
1044 /* Send any queued vblank events, lest the natives grow disquiet */ 1056 /* Send any queued vblank events, lest the natives grow disquiet */
1045 seq = drm_vblank_count_and_time(dev, crtc, &now); 1057 seq = drm_vblank_count_and_time(dev, crtc, &now);
1046 1058
1047 spin_lock(&dev->event_lock);
1048 list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) { 1059 list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) {
1049 if (e->pipe != crtc) 1060 if (e->pipe != crtc)
1050 continue; 1061 continue;
@@ -1055,18 +1066,7 @@ void drm_vblank_off(struct drm_device *dev, int crtc)
1055 drm_vblank_put(dev, e->pipe); 1066 drm_vblank_put(dev, e->pipe);
1056 send_vblank_event(dev, e, seq, &now); 1067 send_vblank_event(dev, e, seq, &now);
1057 } 1068 }
1058 spin_unlock(&dev->event_lock); 1069 spin_unlock_irqrestore(&dev->event_lock, irqflags);
1059
1060 /*
1061 * Prevent subsequent drm_vblank_get() from re-enabling
1062 * the vblank interrupt by bumping the refcount.
1063 */
1064 if (!vblank->inmodeset) {
1065 atomic_inc(&vblank->refcount);
1066 vblank->inmodeset = 1;
1067 }
1068
1069 spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
1070} 1070}
1071EXPORT_SYMBOL(drm_vblank_off); 1071EXPORT_SYMBOL(drm_vblank_off);
1072 1072
@@ -1446,12 +1446,11 @@ static void drm_handle_vblank_events(struct drm_device *dev, int crtc)
1446{ 1446{
1447 struct drm_pending_vblank_event *e, *t; 1447 struct drm_pending_vblank_event *e, *t;
1448 struct timeval now; 1448 struct timeval now;
1449 unsigned long flags;
1450 unsigned int seq; 1449 unsigned int seq;
1451 1450
1452 seq = drm_vblank_count_and_time(dev, crtc, &now); 1451 assert_spin_locked(&dev->event_lock);
1453 1452
1454 spin_lock_irqsave(&dev->event_lock, flags); 1453 seq = drm_vblank_count_and_time(dev, crtc, &now);
1455 1454
1456 list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) { 1455 list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) {
1457 if (e->pipe != crtc) 1456 if (e->pipe != crtc)
@@ -1467,8 +1466,6 @@ static void drm_handle_vblank_events(struct drm_device *dev, int crtc)
1467 send_vblank_event(dev, e, seq, &now); 1466 send_vblank_event(dev, e, seq, &now);
1468 } 1467 }
1469 1468
1470 spin_unlock_irqrestore(&dev->event_lock, flags);
1471
1472 trace_drm_vblank_event(crtc, seq); 1469 trace_drm_vblank_event(crtc, seq);
1473} 1470}
1474 1471
@@ -1491,15 +1488,18 @@ bool drm_handle_vblank(struct drm_device *dev, int crtc)
1491 if (!dev->num_crtcs) 1488 if (!dev->num_crtcs)
1492 return false; 1489 return false;
1493 1490
1491 spin_lock_irqsave(&dev->event_lock, irqflags);
1492
1494 /* Need timestamp lock to prevent concurrent execution with 1493 /* Need timestamp lock to prevent concurrent execution with
1495 * vblank enable/disable, as this would cause inconsistent 1494 * vblank enable/disable, as this would cause inconsistent
1496 * or corrupted timestamps and vblank counts. 1495 * or corrupted timestamps and vblank counts.
1497 */ 1496 */
1498 spin_lock_irqsave(&dev->vblank_time_lock, irqflags); 1497 spin_lock(&dev->vblank_time_lock);
1499 1498
1500 /* Vblank irq handling disabled. Nothing to do. */ 1499 /* Vblank irq handling disabled. Nothing to do. */
1501 if (!vblank->enabled) { 1500 if (!vblank->enabled) {
1502 spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags); 1501 spin_unlock(&dev->vblank_time_lock);
1502 spin_unlock_irqrestore(&dev->event_lock, irqflags);
1503 return false; 1503 return false;
1504 } 1504 }
1505 1505
@@ -1539,10 +1539,13 @@ bool drm_handle_vblank(struct drm_device *dev, int crtc)
1539 crtc, (int) diff_ns); 1539 crtc, (int) diff_ns);
1540 } 1540 }
1541 1541
1542 spin_unlock(&dev->vblank_time_lock);
1543
1542 wake_up(&vblank->queue); 1544 wake_up(&vblank->queue);
1543 drm_handle_vblank_events(dev, crtc); 1545 drm_handle_vblank_events(dev, crtc);
1544 1546
1545 spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags); 1547 spin_unlock_irqrestore(&dev->event_lock, irqflags);
1548
1546 return true; 1549 return true;
1547} 1550}
1548EXPORT_SYMBOL(drm_handle_vblank); 1551EXPORT_SYMBOL(drm_handle_vblank);