drm: Fix race between drm_vblank_off() and drm_queue_vblank_event()
Currently it's possible that the following will happen: 1. drm_wait_vblank() calls drm_vblank_get() 2. drm_vblank_off() gets called 3. drm_wait_vblank() calls drm_queue_vblank_event() which adds the event to the queue event though vblank interrupts are currently disabled (and may not be re-enabled ever again). To fix the problem, add another vblank->enabled check into drm_queue_vblank_event(). drm_vblank_off() holds event_lock around the vblank disable, so no further locking needs to be added to drm_queue_vblank_event(). vblank disable from another source is not possible since drm_wait_vblank() already holds a vblank reference. 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>
This commit is contained in:
parent
56cc279b29
commit
ffe7c73a8d
|
@ -1270,6 +1270,7 @@ static int drm_queue_vblank_event(struct drm_device *dev, int pipe,
|
|||
union drm_wait_vblank *vblwait,
|
||||
struct drm_file *file_priv)
|
||||
{
|
||||
struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
|
||||
struct drm_pending_vblank_event *e;
|
||||
struct timeval now;
|
||||
unsigned long flags;
|
||||
|
@ -1293,6 +1294,18 @@ static int drm_queue_vblank_event(struct drm_device *dev, int pipe,
|
|||
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
|
||||
/*
|
||||
* drm_vblank_off() might have been called after we called
|
||||
* drm_vblank_get(). drm_vblank_off() holds event_lock
|
||||
* around the vblank disable, so no need for further locking.
|
||||
* The reference from drm_vblank_get() protects against
|
||||
* vblank disable from another source.
|
||||
*/
|
||||
if (!vblank->enabled) {
|
||||
ret = -EINVAL;
|
||||
goto err_unlock;
|
||||
}
|
||||
|
||||
if (file_priv->event_space < sizeof e->event) {
|
||||
ret = -EBUSY;
|
||||
goto err_unlock;
|
||||
|
|
Loading…
Reference in New Issue