Per fdo bz #18041, we timeout if a vblank event doesn't occur within a specified time period. However, this wait happens in libdrm, so old userspace can still end up hanging, waiting for a vblank event that may never occur. So add a more reasonable timeout value to drm_wait_vblank and make the wait uninterruptible, so that the X server's scheduling signals don't get in the way.
Reported-by: Linus Torvalds <torva...@linux-foundation.org> Tested-by: Linus Torvalds <torva...@linux-foundation.org> Signed-off-by: Jesse Barnes <jbar...@virtuousgeek.org> diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 69aa0ab..c5df063 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -539,9 +539,11 @@ out: int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_priv) { + struct timeval now; union drm_wait_vblank *vblwait = data; int ret = 0; unsigned int flags, seq, crtc; + int timeout, max_wait = (DRM_HZ / 40) * 100; /* 100ms max */ if ((!dev->pdev->irq) || (!dev->irq_enabled)) return -EINVAL; @@ -588,25 +590,24 @@ int drm_wait_vblank(struct drm_device *dev, void *data, DRM_DEBUG("waiting on vblank count %d, crtc %d\n", vblwait->request.sequence, crtc); + + /* Wait at most 25ms * number of frames */ + timeout = (DRM_HZ / 40) * (vblwait->request.sequence - seq); + if (timeout > max_wait) + timeout = max_wait; + dev->last_vblank_wait[crtc] = vblwait->request.sequence; - DRM_WAIT_ON(ret, dev->vbl_queue[crtc], 3 * DRM_HZ, + DRM_WAIT_ON_UNINTERRUPTIBLE(ret, dev->vbl_queue[crtc], timeout, (((drm_vblank_count(dev, crtc) - vblwait->request.sequence) <= (1 << 23)) || !dev->irq_enabled)); - if (ret != -EINTR) { - struct timeval now; + do_gettimeofday(&now); - do_gettimeofday(&now); - - vblwait->reply.tval_sec = now.tv_sec; - vblwait->reply.tval_usec = now.tv_usec; - vblwait->reply.sequence = drm_vblank_count(dev, crtc); - DRM_DEBUG("returning %d to client\n", - vblwait->reply.sequence); - } else { - DRM_DEBUG("vblank wait interrupted by signal\n"); - } + vblwait->reply.tval_sec = now.tv_sec; + vblwait->reply.tval_usec = now.tv_usec; + vblwait->reply.sequence = drm_vblank_count(dev, crtc); + DRM_DEBUG("returning %d to client\n", vblwait->reply.sequence); done: drm_vblank_put(dev, crtc); diff --git a/include/drm/drm_os_linux.h b/include/drm/drm_os_linux.h index 8dbd257..67afee8 100644 --- a/include/drm/drm_os_linux.h +++ b/include/drm/drm_os_linux.h @@ -104,5 +104,25 @@ do { \ remove_wait_queue(&(queue), &entry); \ } while (0) +#define DRM_WAIT_ON_UNINTERRUPTIBLE( ret, queue, timeout, condition ) \ +do { \ + DECLARE_WAITQUEUE(entry, current); \ + unsigned long end = jiffies + (timeout); \ + add_wait_queue(&(queue), &entry); \ + \ + for (;;) { \ + __set_current_state(TASK_UNINTERRUPTIBLE); \ + if (condition) \ + break; \ + if (time_after_eq(jiffies, end)) { \ + ret = -EBUSY; \ + break; \ + } \ + schedule_timeout((HZ/100 > 1) ? HZ/100 : 1); \ + } \ + __set_current_state(TASK_RUNNING); \ + remove_wait_queue(&(queue), &entry); \ +} while (0) + #define DRM_WAKEUP( queue ) wake_up_interruptible( queue ) #define DRM_INIT_WAITQUEUE( queue ) init_waitqueue_head( queue ) ------------------------------------------------------------------------------ This SF.net email is sponsored by: SourcForge Community SourceForge wants to tell your story. http://p.sf.net/sfu/sf-spreadtheword -- _______________________________________________ Dri-devel mailing list Dri-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/dri-devel