summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Wilson <chris@chris-wilson.co.uk>2016-03-19 13:04:02 +0000
committerChris Wilson <chris@chris-wilson.co.uk>2016-03-19 15:20:43 +0000
commitd54561086189d3bc83403d0ae19a389d0a68f89d (patch)
tree7c37fe96a0e718abba651d2fd9a50e6c765c9247
parentc1fed522ae98b7c74700521f7b3e0368f0f0948e (diff)
lib/igt_aux: Divert ioctls for signal injection
To simplify and speed up running interruptible tests, use a custom ioctl() function that control the signaling and detect when we need no more iterations to trigger an interruption. We use a realtime timer to inject the signal after a certain delay, increasing the delay on every loop to try and exercise different code paths within the function. The first delay is very short such that we hopefully enter the kernel with a pending signal. Clients should use struct igt_sigiter iter = {}; while (igt_sigiter_repeat(&iter, enable_interrupts=true)) do_test() to automatically repeat the test until we can inject no more signals into the ioctls. This is condensed into a macro igt_interruptible(enable_interrupts=true) do_test(); for convenience. Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
-rw-r--r--benchmarks/Makefile.am2
-rw-r--r--configure.ac14
-rw-r--r--debugger/Makefile.am2
-rw-r--r--demos/Makefile.am2
-rw-r--r--lib/Makefile.am2
-rw-r--r--lib/igt_aux.c188
-rw-r--r--lib/igt_aux.h8
-rw-r--r--lib/tests/Makefile.am2
-rw-r--r--tests/Makefile.am2
-rw-r--r--tools/Makefile.am2
10 files changed, 217 insertions, 7 deletions
diff --git a/benchmarks/Makefile.am b/benchmarks/Makefile.am
index a555ab6a..c67f4722 100644
--- a/benchmarks/Makefile.am
+++ b/benchmarks/Makefile.am
@@ -3,7 +3,7 @@ include Makefile.sources
AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/lib
AM_CFLAGS = $(DRM_CFLAGS) $(CWARNFLAGS) $(CAIRO_CFLAGS) $(LIBUNWIND_CFLAGS)
-LDADD = $(top_builddir)/lib/libintel_tools.la $(DRM_LIBS) $(PCIACCESS_LIBS) $(CAIRO_LIBS) $(LIBUNWIND_LIBS) -lm
+LDADD = $(top_builddir)/lib/libintel_tools.la $(DRM_LIBS) $(PCIACCESS_LIBS) $(CAIRO_LIBS) $(LIBUNWIND_LIBS) $(TIMER_LIBS) -lm
benchmarks_LTLIBRARIES = gem_exec_tracer.la
gem_exec_tracer_la_LDFLAGS = -module -avoid-version -no-undefined
diff --git a/configure.ac b/configure.ac
index e523b7a8..1024ad80 100644
--- a/configure.ac
+++ b/configure.ac
@@ -66,6 +66,20 @@ AC_CHECK_TYPES([sighandler_t],[],[],[AC_INCLUDES_DEFAULT
AC_CHECK_FUNCS([swapctl])
AC_CHECK_FUNCS([asprintf])
+dnl Check for POSIX timers
+AC_CHECK_FUNCS(timer_create, [], [
+ AC_CHECK_LIB(rt, timer_create, [
+ AC_DEFINE(HAVE_TIMER_CREATE, 1)
+ TIMER_LIBS="-lrt"
+ ], [
+ AC_CHECK_LIB(pthread, timer_create, [
+ AC_DEFINE(HAVE_TIMER_CREATE, 1)
+ TIMER_LIBS="-lpthread"
+ ])
+ ])
+])
+AC_SUBST(TIMER_LIBS)
+
# Initialize libtool
AC_DISABLE_STATIC
AC_PROG_LIBTOOL
diff --git a/debugger/Makefile.am b/debugger/Makefile.am
index 0b6028b4..5a523f5e 100644
--- a/debugger/Makefile.am
+++ b/debugger/Makefile.am
@@ -15,4 +15,4 @@ AM_CFLAGS = \
$(LIBUNWIND_CFLAGS) \
$(CWARNFLAGS)
-LDADD = $(top_builddir)/lib/libintel_tools.la $(DRM_LIBS) $(PCIACCESS_LIBS) $(CAIRO_LIBS) $(LIBUNWIND_LIBS)
+LDADD = $(top_builddir)/lib/libintel_tools.la $(DRM_LIBS) $(PCIACCESS_LIBS) $(CAIRO_LIBS) $(LIBUNWIND_LIBS) $(TIMER_LIBS)
diff --git a/demos/Makefile.am b/demos/Makefile.am
index 029581a6..d18a705f 100644
--- a/demos/Makefile.am
+++ b/demos/Makefile.am
@@ -4,4 +4,4 @@ bin_PROGRAMS = \
AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/lib
AM_CFLAGS = $(DRM_CFLAGS) $(PCIACCESS_CFLAGS) $(CWARNFLAGS) $(CAIRO_CFLAGS) $(LIBUNWIND_CFLAGS)
-LDADD = $(top_builddir)/lib/libintel_tools.la $(DRM_LIBS) $(PCIACCESS_LIBS) $(CAIRO_LIBS) $(LIBUNWIND_LIBS)
+LDADD = $(top_builddir)/lib/libintel_tools.la $(DRM_LIBS) $(PCIACCESS_LIBS) $(CAIRO_LIBS) $(LIBUNWIND_LIBS) $(TIMER_LIBS)
diff --git a/lib/Makefile.am b/lib/Makefile.am
index e3a456ba..a8a1eb6d 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -21,6 +21,6 @@ AM_CFLAGS = $(DRM_CFLAGS) $(CWARNFLAGS) $(LIBUNWIND_CFLAGS) $(DEBUG_CFLAGS) \
-DIGT_LOG_DOMAIN=\""$(subst _,-,$*)"\" \
-pthread
-LDADD = $(CAIRO_LIBS) $(LIBUNWIND_LIBS) -lm
+LDADD = $(CAIRO_LIBS) $(LIBUNWIND_LIBS) $(TIMER_LIBS) -lm
AM_CFLAGS += $(CAIRO_CFLAGS)
diff --git a/lib/igt_aux.c b/lib/igt_aux.c
index f38ecd84..336440c9 100644
--- a/lib/igt_aux.c
+++ b/lib/igt_aux.c
@@ -40,6 +40,7 @@
#include <signal.h>
#include <pciaccess.h>
#include <stdlib.h>
+#include <time.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/time.h>
@@ -73,6 +74,193 @@
/* signal interrupt helpers */
+
+#define MSEC_PER_SEC (1000)
+#define USEC_PER_SEC (1000*MSEC_PER_SEC)
+#define NSEC_PER_SEC (1000*USEC_PER_SEC)
+
+/* signal interrupt helpers */
+#define gettid() syscall(__NR_gettid)
+#define sigev_notify_thread_id _sigev_un._tid
+
+static struct __igt_sigiter {
+ pid_t tid;
+ timer_t timer;
+ struct timespec offset;
+ struct {
+ long hit, miss;
+ long ioctls, signals;
+ } stat;
+} __igt_sigiter;
+
+static void sigiter(int sig, siginfo_t *info, void *arg)
+{
+ __igt_sigiter.stat.signals++;
+}
+
+#if 0
+#define SIG_ASSERT(expr) igt_assert(expr)
+#else
+#define SIG_ASSERT(expr)
+#endif
+
+static int
+sig_ioctl(int fd, unsigned long request, void *arg)
+{
+ struct itimerspec its;
+ int ret;
+
+ SIG_ASSERT(__igt_sigiter.timer);
+ SIG_ASSERT(__igt_sigiter.tid == gettid());
+
+ memset(&its, 0, sizeof(its));
+ its.it_value = __igt_sigiter.offset;
+ do {
+ long serial;
+
+ __igt_sigiter.stat.ioctls++;
+
+ ret = 0;
+ serial = __igt_sigiter.stat.signals;
+ igt_assert(timer_settime(__igt_sigiter.timer, 0, &its, NULL) == 0);
+ if (ioctl(fd, request, arg))
+ ret = errno;
+ if (__igt_sigiter.stat.signals == serial)
+ __igt_sigiter.stat.miss++;
+ if (ret == 0)
+ break;
+
+ if (ret == EINTR) {
+ __igt_sigiter.stat.hit++;
+
+ its.it_value.tv_sec *= 2;
+ its.it_value.tv_nsec *= 2;
+ while (its.it_value.tv_nsec >= NSEC_PER_SEC) {
+ its.it_value.tv_nsec -= NSEC_PER_SEC;
+ its.it_value.tv_sec += 1;
+ }
+
+ SIG_ASSERT(its.it_value.tv_nsec >= 0);
+ SIG_ASSERT(its.it_value.tv_sec >= 0);
+ }
+ } while (ret == EAGAIN || ret == EINTR);
+
+ memset(&its, 0, sizeof(its));
+ timer_settime(__igt_sigiter.timer, 0, &its, NULL);
+
+ errno = ret;
+ return ret ? -1 : 0;
+}
+
+static bool igt_sigiter_start(struct igt_sigiter *iter, bool enable)
+{
+ /* Note that until we can automatically clean up on failed/skipped
+ * tests, we cannot assume the state of the igt_ioctl indirection.
+ */
+ SIG_ASSERT(igt_ioctl == drmIoctl);
+ igt_ioctl = drmIoctl;
+
+ if (enable) {
+ struct sigevent sev;
+ struct sigaction act;
+
+ igt_ioctl = sig_ioctl;
+ __igt_sigiter.tid = gettid();
+
+ memset(&sev, 0, sizeof(sev));
+ sev.sigev_notify = SIGEV_SIGNAL | SIGEV_THREAD_ID;
+ sev.sigev_notify_thread_id = __igt_sigiter.tid;
+ sev.sigev_signo = SIGRTMIN;
+ igt_assert(timer_create(CLOCK_MONOTONIC, &sev, &__igt_sigiter.timer) == 0);
+
+ memset(&act, 0, sizeof(act));
+ act.sa_sigaction = sigiter;
+ act.sa_flags = SA_SIGINFO;
+ igt_assert(sigaction(SIGRTMIN, &act, NULL) == 0);
+
+ __igt_sigiter.offset.tv_sec = 0;
+ __igt_sigiter.offset.tv_nsec = 50;
+ }
+
+ return true;
+}
+
+static bool igt_sigiter_stop(struct igt_sigiter *iter, bool enable)
+{
+ if (enable) {
+ struct sigaction act;
+
+ SIG_ASSERT(igt_ioctl == sig_ioctl);
+ SIG_ASSERT(__igt_sigiter.tid == gettid());
+ igt_ioctl = drmIoctl;
+
+ timer_delete(__igt_sigiter.timer);
+
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = SIG_IGN;
+ sigaction(SIGRTMIN, &act, NULL);
+
+ memset(&__igt_sigiter, 0, sizeof(__igt_sigiter));
+ }
+
+ memset(iter, 0, sizeof(*iter));
+ return false;
+}
+
+/**
+ * igt_sigiter_continue:
+ * @iter: the control struct
+ * @enable: a boolean as to whether or not we want to enable interruptions
+ *
+ * Provides control flow such that all drmIoctl() (strictly igt_ioctl())
+ * within the loop are forcibly injected with signals (SIGRTMIN).
+ *
+ * This is useful to exercise ioctl error paths, at least where those can be
+ * exercises by interrupting blocking waits, like stalling for the gpu.
+ *
+ * igt_sigiter_continue() returns false when it has detected that it
+ * cannot inject any more signals in the ioctls from previous runs.
+ *
+ * Typical usage is
+ * struct igt_sigiter iter = {};
+ * while (igt_sigiter_continue(&iter, test_flags & TEST_INTERRUPTIBLE))
+ * do_test();
+ *
+ * This is condensed into the igt_interruptible() macro.
+ *
+ * Note that since this overloads the igt_ioctl(), this method is not useful
+ * for widespread signal injection, for example providing coverage of
+ * pagefaults. To interrupt everything, see igt_fork_signal_helper().
+ */
+bool igt_sigiter_continue(struct igt_sigiter *iter, bool enable)
+{
+ if (iter->pass++ == 0)
+ return igt_sigiter_start(iter, enable);
+
+ if (__igt_sigiter.stat.miss == __igt_sigiter.stat.ioctls)
+ return igt_sigiter_stop(iter, enable);
+
+ igt_debug("%s: pass %d, missed %ld/%ld\n",
+ __func__, iter->pass - 1,
+ __igt_sigiter.stat.miss,
+ __igt_sigiter.stat.ioctls);
+
+ SIG_ASSERT(igt_ioctl == sig_ioctl);
+ SIG_ASSERT(__igt_sigiter.timer);
+
+ __igt_sigiter.offset.tv_sec *= 2;
+ __igt_sigiter.offset.tv_nsec *= 2;
+ while (__igt_sigiter.offset.tv_nsec >= NSEC_PER_SEC) {
+ __igt_sigiter.offset.tv_nsec -= NSEC_PER_SEC;
+ __igt_sigiter.offset.tv_sec += 1;
+ }
+ SIG_ASSERT(__igt_sigiter.offset.tv_nsec >= 0);
+ SIG_ASSERT(__igt_sigiter.offset.tv_sec >= 0);
+
+ memset(&__igt_sigiter.stat, 0, sizeof(__igt_sigiter.stat));
+ return true;
+}
+
static struct igt_helper_process signal_helper;
long long int sig_stat;
static void __attribute__((noreturn)) signal_helper_process(pid_t pid)
diff --git a/lib/igt_aux.h b/lib/igt_aux.h
index 427719ef..eabeefd2 100644
--- a/lib/igt_aux.h
+++ b/lib/igt_aux.h
@@ -40,6 +40,14 @@ extern int num_trash_bos;
void igt_fork_signal_helper(void);
void igt_stop_signal_helper(void);
+struct igt_sigiter {
+ unsigned pass;
+};
+
+bool igt_sigiter_continue(struct igt_sigiter *iter, bool interrupt);
+#define igt_interruptible(E) \
+ for (struct igt_sigiter iter__={}; igt_sigiter_continue(&iter__, (E)); )
+
void igt_exchange_int(void *array, unsigned i, unsigned j);
void igt_permute_array(void *array, unsigned size,
void (*exchange_func)(void *array,
diff --git a/lib/tests/Makefile.am b/lib/tests/Makefile.am
index f09d2fe7..582cc3e9 100644
--- a/lib/tests/Makefile.am
+++ b/lib/tests/Makefile.am
@@ -13,7 +13,7 @@ AM_CFLAGS = $(DRM_CFLAGS) $(CWARNFLAGS) $(DEBUG_CFLAGS) \
-DIGT_DATADIR=\""$(abs_srcdir)"\" \
$(NULL)
-LDADD = ../libintel_tools.la $(PCIACCESS_LIBS) $(DRM_LIBS) $(LIBUNWIND_LIBS)
+LDADD = ../libintel_tools.la $(PCIACCESS_LIBS) $(DRM_LIBS) $(LIBUNWIND_LIBS) $(TIMER_LIBS)
LDADD += $(CAIRO_LIBS) $(LIBUDEV_LIBS) $(GLIB_LIBS) -lm
AM_CFLAGS += $(CAIRO_CFLAGS) $(LIBUDEV_CFLAGS) $(GLIB_CFLAGS)
diff --git a/tests/Makefile.am b/tests/Makefile.am
index f72f7c0f..0ed40f7d 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -56,7 +56,7 @@ AM_CFLAGS = $(DRM_CFLAGS) $(CWARNFLAGS) $(DEBUG_CFLAGS)\
$(LIBUNWIND_CFLAGS) \
$(NULL)
-LDADD = ../lib/libintel_tools.la $(PCIACCESS_LIBS) $(DRM_LIBS) $(LIBUNWIND_LIBS)
+LDADD = ../lib/libintel_tools.la $(PCIACCESS_LIBS) $(DRM_LIBS) $(LIBUNWIND_LIBS) $(TIMER_LIBS)
LDADD += $(CAIRO_LIBS) $(LIBUDEV_LIBS) $(GLIB_LIBS) -lm
AM_CFLAGS += $(CAIRO_CFLAGS) $(LIBUDEV_CFLAGS) $(GLIB_CFLAGS)
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 90a8ec1b..74c55218 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -4,7 +4,7 @@ SUBDIRS = null_state_gen registers
AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/lib
AM_CFLAGS = $(DEBUG_CFLAGS) $(DRM_CFLAGS) $(PCIACCESS_CFLAGS) $(CWARNFLAGS) $(CAIRO_CFLAGS) $(LIBUNWIND_CFLAGS) -DPKGDATADIR=\"$(pkgdatadir)\"
-LDADD = $(top_builddir)/lib/libintel_tools.la $(DRM_LIBS) $(PCIACCESS_LIBS) $(CAIRO_LIBS) $(LIBUDEV_LIBS) $(LIBUNWIND_LIBS) -lm
+LDADD = $(top_builddir)/lib/libintel_tools.la $(DRM_LIBS) $(PCIACCESS_LIBS) $(CAIRO_LIBS) $(LIBUDEV_LIBS) $(LIBUNWIND_LIBS) $(TIMER_LIBS) -lm
AM_LDFLAGS = -Wl,--as-needed