OSDN Git Service

libdrm: add timeout handling to drmWaitVBlank
authorJesse Barnes <jbarnes@virtuousgeek.org>
Wed, 7 Jan 2009 18:18:08 +0000 (10:18 -0800)
committerJesse Barnes <jbarnes@virtuousgeek.org>
Wed, 7 Jan 2009 18:18:08 +0000 (10:18 -0800)
In some cases, vblank interrupts may be disabled or otherwise broken.
The kernel has a 3s timeout builtin to handle these cases, but the X
server's SIGALM for cursor handling may interrupt vblank wait ioctls,
causing libdrm to restart the ioctl, making the kernel's timeout
useless.

This change tracks time across ioctl restarts and returns EBUSY to the
caller if the expected vblank sequence doesn't occur within 1s of the
first call.

Fixes fdo bz #18041, which is caused by a drmWaitVBlank hanging due to
the corresponding pipe getting disabled (thus preventing further events
from coming in).

libdrm/Makefile.am
libdrm/xf86drm.c

index eb63abe..71a8718 100644 (file)
@@ -23,6 +23,7 @@ SUBDIRS = . intel
 libdrm_la_LTLIBRARIES = libdrm.la
 libdrm_ladir = $(libdir)
 libdrm_la_LDFLAGS = -version-number 2:4:0 -no-undefined
+libdrm_la_LIBADD = -lrt
 
 AM_CFLAGS = -I$(top_srcdir)/shared-core
 libdrm_la_SOURCES = xf86drm.c xf86drmHash.c xf86drmRandom.c xf86drmSL.c \
index 0b5d31f..3396e28 100644 (file)
@@ -42,6 +42,7 @@
 #include <fcntl.h>
 #include <errno.h>
 #include <signal.h>
+#include <time.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #define stat_t struct stat
@@ -1896,13 +1897,30 @@ int drmScatterGatherFree(int fd, drm_handle_t handle)
  */
 int drmWaitVBlank(int fd, drmVBlankPtr vbl)
 {
+    struct timespec timeout, cur;
     int ret;
 
+    ret = clock_gettime(CLOCK_MONOTONIC, &timeout);
+    if (ret < 0) {
+       fprintf(stderr, "clock_gettime failed: %s\n", strerror(ret));
+       goto out;
+    }
+    timeout.tv_sec++;
+
     do {
-       ret = drmIoctl(fd, DRM_IOCTL_WAIT_VBLANK, vbl);
+       ret = ioctl(fd, DRM_IOCTL_WAIT_VBLANK, vbl);
        vbl->request.type &= ~DRM_VBLANK_RELATIVE;
+       clock_gettime(CLOCK_MONOTONIC, &cur);
+       /* Timeout after 1s */
+       if (cur.tv_sec > timeout.tv_sec + 1 ||
+          cur.tv_sec == timeout.tv_sec && cur.tv_nsec >= timeout.tv_nsec) {
+          errno = EBUSY;
+          ret = -1;
+          break;
+       }
     } while (ret && errno == EINTR);
 
+out:
     return ret;
 }