summaryrefslogtreecommitdiff
path: root/lib
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 /lib
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>
Diffstat (limited to 'lib')
-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
4 files changed, 198 insertions, 2 deletions
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)