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:
Родитель
56cc279b29
Коммит
ffe7c73a8d
|
@ -1270,6 +1270,7 @@ static int drm_queue_vblank_event(struct drm_device *dev, int pipe,
|
||||||
union drm_wait_vblank *vblwait,
|
union drm_wait_vblank *vblwait,
|
||||||
struct drm_file *file_priv)
|
struct drm_file *file_priv)
|
||||||
{
|
{
|
||||||
|
struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
|
||||||
struct drm_pending_vblank_event *e;
|
struct drm_pending_vblank_event *e;
|
||||||
struct timeval now;
|
struct timeval now;
|
||||||
unsigned long flags;
|
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);
|
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) {
|
if (file_priv->event_space < sizeof e->event) {
|
||||||
ret = -EBUSY;
|
ret = -EBUSY;
|
||||||
goto err_unlock;
|
goto err_unlock;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче