summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Wilson <chris@chris-wilson.co.uk>2016-10-12 14:41:46 +0100
committerChris Wilson <chris@chris-wilson.co.uk>2016-10-13 09:32:44 +0100
commit9c2be694164cd654e4e15cdd9f15da7127ad6176 (patch)
treeafb5eaf6426edbea0f69fe9a05b937691713b490
parentd21094516d2e93468b5d24904a334cb48cc2852f (diff)
igt/gem_wait: Use explicit timers
Rather than guestimating a workload that should take a certain amount of time, use a sigitimer to terminate a batch (and so complete the wait) after an exact amount of time. And in the process expand testing to cover multiple rings and hangcheck. Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
-rw-r--r--lib/igt_core.h27
-rw-r--r--tests/gem_wait.c443
2 files changed, 277 insertions, 193 deletions
diff --git a/lib/igt_core.h b/lib/igt_core.h
index 433b88c9..03be757c 100644
--- a/lib/igt_core.h
+++ b/lib/igt_core.h
@@ -403,6 +403,24 @@ static inline void igt_ignore_warn(bool value)
} while (0)
/**
+ * igt_assert_cmps64:
+ * @n1: first value
+ * @cmp: compare operator
+ * @ncmp: negated version of @cmp
+ * @n2: second value
+ *
+ * Like igt_assert_cmpuint(), but for larger signed ints.
+ */
+#define igt_assert_cmps64(n1, cmp, ncmp, n2) \
+ do { \
+ int64_t __n1 = (n1), __n2 = (n2); \
+ if (__n1 cmp __n2) ; else \
+ __igt_fail_assert(IGT_LOG_DOMAIN, __FILE__, __LINE__, __func__, \
+ #n1 " " #cmp " " #n2, \
+ "error: %lld " #ncmp " %lld\n", (long long)__n1, (long long)__n2); \
+ } while (0)
+
+/**
* igt_assert_cmpu64:
* @n1: first value
* @cmp: compare operator
@@ -461,6 +479,15 @@ static inline void igt_ignore_warn(bool value)
#define igt_assert_eq_u32(n1, n2) igt_assert_cmpuint(n1, ==, !=, n2)
/**
+ * igt_assert_eq_s64:
+ * @n1: first integer
+ * @n2: second integer
+ *
+ * Like igt_assert_eq_u32(), but for int64_t.
+ */
+#define igt_assert_eq_s64(n1, n2) igt_assert_cmps64(n1, ==, !=, n2)
+
+/**
* igt_assert_eq_u64:
* @n1: first integer
* @n2: second integer
diff --git a/tests/gem_wait.c b/tests/gem_wait.c
index 461efdbd..0ecb92f4 100644
--- a/tests/gem_wait.c
+++ b/tests/gem_wait.c
@@ -26,233 +26,290 @@
*/
#include "igt.h"
-#include <stdio.h>
+
+#include <signal.h>
#include <time.h>
-#include <stdlib.h>
-#include <sys/ioctl.h>
-#include <stdio.h>
-#include <string.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <errno.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-
-#include <drm.h>
-
-#include "intel_bufmgr.h"
-
-#define MSEC_PER_SEC 1000L
-#define USEC_PER_MSEC 1000L
-#define NSEC_PER_USEC 1000L
-#define NSEC_PER_MSEC 1000000L
-#define USEC_PER_SEC 1000000L
-#define NSEC_PER_SEC 1000000000L
-
-#define ENOUGH_WORK_IN_SECONDS 2
-#define BUF_SIZE (8<<20)
-#define BUF_PAGES ((8<<20)>>12)
-drm_intel_bo *dst, *dst2;
-
-/* returns time diff in milliseconds */
-static int64_t
-do_time_diff(struct timespec *end, struct timespec *start)
-{
- int64_t ret;
- ret = (MSEC_PER_SEC * difftime(end->tv_sec, start->tv_sec)) +
- ((end->tv_nsec/NSEC_PER_MSEC) - (start->tv_nsec/NSEC_PER_MSEC));
- return ret;
-}
+#include <sys/syscall.h>
-static void blt_color_fill(struct intel_batchbuffer *batch,
- drm_intel_bo *buf,
- const unsigned int pages)
-{
- const unsigned short height = pages/4;
- const unsigned short width = 4096;
-
- COLOR_BLIT_COPY_BATCH_START(COLOR_BLT_WRITE_ALPHA |
- XY_COLOR_BLT_WRITE_RGB);
- OUT_BATCH((3 << 24) | /* 32 Bit Color */
- (0xF0 << 16) | /* Raster OP copy background register */
- 0); /* Dest pitch is 0 */
- OUT_BATCH(0);
- OUT_BATCH(width << 16 |
- height);
- OUT_RELOC_FENCED(buf, I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER, 0);
- OUT_BATCH(rand()); /* random pattern */
- ADVANCE_BATCH();
-}
+#define gettid() syscall(__NR_gettid)
+#define sigev_notify_thread_id _sigev_un._tid
-static void render_timeout(int fd)
+#define LOCAL_I915_EXEC_BSD_SHIFT (13)
+#define LOCAL_I915_EXEC_BSD_MASK (3 << LOCAL_I915_EXEC_BSD_SHIFT)
+
+#define ENGINE_MASK (I915_EXEC_RING_MASK | LOCAL_I915_EXEC_BSD_MASK)
+
+static int __gem_wait(int fd, struct drm_i915_gem_wait *w)
{
- drm_intel_bufmgr *bufmgr;
- struct intel_batchbuffer *batch;
- int64_t timeout = ENOUGH_WORK_IN_SECONDS * NSEC_PER_SEC;
- int64_t negative_timeout = -1;
- int ret;
- const bool do_signals = true; /* signals will seem to make the operation
- * use less process CPU time */
- bool done = false;
- int i, iter = 1;
+ int err;
- igt_skip_on_simulation();
+ err = 0;
+ if (igt_ioctl(fd, DRM_IOCTL_I915_GEM_WAIT, w))
+ err = -errno;
- bufmgr = drm_intel_bufmgr_gem_init(fd, 4096);
- drm_intel_bufmgr_gem_enable_reuse(bufmgr);
- batch = intel_batchbuffer_alloc(bufmgr, intel_get_drm_devid(fd));
-
- dst = drm_intel_bo_alloc(bufmgr, "dst", BUF_SIZE, 4096);
- dst2 = drm_intel_bo_alloc(bufmgr, "dst2", BUF_SIZE, 4096);
-
- igt_skip_on_f(gem_wait(fd, dst->handle, &timeout) == -EINVAL,
- "kernel doesn't support wait_timeout, skipping test\n");
- timeout = ENOUGH_WORK_IN_SECONDS * NSEC_PER_SEC;
-
- /* Figure out a rough number of fills required to consume 1 second of
- * GPU work.
- */
- do {
- struct timespec start, end;
- long diff;
-
-#ifndef CLOCK_MONOTONIC_RAW
-#define CLOCK_MONOTONIC_RAW CLOCK_MONOTONIC
-#endif
-
- igt_assert(clock_gettime(CLOCK_MONOTONIC_RAW, &start) == 0);
- for (i = 0; i < iter; i++)
- blt_color_fill(batch, dst, BUF_PAGES);
- intel_batchbuffer_flush(batch);
- drm_intel_bo_wait_rendering(dst);
- igt_assert(clock_gettime(CLOCK_MONOTONIC_RAW, &end) == 0);
-
- diff = do_time_diff(&end, &start);
- igt_assert(diff >= 0);
-
- if ((diff / MSEC_PER_SEC) > ENOUGH_WORK_IN_SECONDS)
- done = true;
- else
- iter <<= 1;
- } while (!done && iter < 1000000);
-
- igt_assert_lt(iter, 1000000);
-
- igt_debug("%d iters is enough work\n", iter);
- gem_quiescent_gpu(fd);
- if (do_signals)
- igt_fork_signal_helper();
-
- /* We should be able to do half as much work in the same amount of time,
- * but because we might schedule almost twice as much as required, we
- * might accidentally time out. Hence add some fudge. */
- for (i = 0; i < iter/3; i++)
- blt_color_fill(batch, dst2, BUF_PAGES);
-
- intel_batchbuffer_flush(batch);
- igt_assert(gem_bo_busy(fd, dst2->handle) == true);
-
- igt_assert_eq(gem_wait(fd, dst2->handle, &timeout), 0);
- igt_assert(gem_bo_busy(fd, dst2->handle) == false);
- igt_assert_neq(timeout, 0);
- if (timeout == (ENOUGH_WORK_IN_SECONDS * NSEC_PER_SEC))
- igt_info("Buffer was already done!\n");
- else
- igt_info("Finished with %fs remaining\n", timeout*1e-9);
-
- /* check that polling with timeout=0 works. */
- timeout = 0;
- igt_assert_eq(gem_wait(fd, dst2->handle, &timeout), 0);
- igt_assert_eq(timeout, 0);
-
- /* Now check that we correctly time out, twice the auto-tune load should
- * be good enough. */
- timeout = ENOUGH_WORK_IN_SECONDS * NSEC_PER_SEC;
- for (i = 0; i < iter*2; i++)
- blt_color_fill(batch, dst2, BUF_PAGES);
-
- intel_batchbuffer_flush(batch);
-
- ret = gem_wait(fd, dst2->handle, &timeout);
- igt_assert_eq(ret, -ETIME);
- igt_assert_eq(timeout, 0);
- igt_assert(gem_bo_busy(fd, dst2->handle) == true);
-
- /* check that polling with timeout=0 works. */
- timeout = 0;
- igt_assert_eq(gem_wait(fd, dst2->handle, &timeout), -ETIME);
- igt_assert_eq(timeout, 0);
-
-
- /* Now check that we can pass negative (infinite) timeouts. */
- negative_timeout = -1;
- for (i = 0; i < iter; i++)
- blt_color_fill(batch, dst2, BUF_PAGES);
-
- intel_batchbuffer_flush(batch);
-
- igt_assert_eq(gem_wait(fd, dst2->handle, &negative_timeout), 0);
- igt_assert_eq(negative_timeout, -1); /* infinity always remains */
- igt_assert(gem_bo_busy(fd, dst2->handle) == false);
-
- if (do_signals)
- igt_stop_signal_helper();
- drm_intel_bo_unreference(dst2);
- drm_intel_bo_unreference(dst);
- intel_batchbuffer_free(batch);
- drm_intel_bufmgr_destroy(bufmgr);
+ return err;
}
static void invalid_flags(int fd)
{
struct drm_i915_gem_wait wait;
- int ret;
- uint32_t handle;
- handle = gem_create(fd, 4096);
-
- wait.bo_handle = handle;
+ memset(&wait, 0, sizeof(wait));
+ wait.bo_handle = gem_create(fd, 4096);
wait.timeout_ns = 1;
/* NOTE: This test intentionally tests for just the next available flag.
* Don't "fix" this testcase without the ABI testcases for new flags
* first. */
wait.flags = 1;
- ret = drmIoctl(fd, DRM_IOCTL_I915_GEM_WAIT, &wait);
- igt_assert(ret != 0 && errno == EINVAL);
+ igt_assert_eq(__gem_wait(fd, &wait), -EINVAL);
- gem_close(fd, handle);
+ gem_close(fd, wait.bo_handle);
}
static void invalid_buf(int fd)
{
struct drm_i915_gem_wait wait;
- int ret;
- wait.bo_handle = 0;
- wait.timeout_ns = 1;
- wait.flags = 0;
- ret = drmIoctl(fd, DRM_IOCTL_I915_GEM_WAIT, &wait);
+ memset(&wait, 0, sizeof(wait));
+ igt_assert_eq(__gem_wait(fd, &wait), -ENOENT);
+}
+
+static uint32_t *batch;
- igt_assert(ret != 0 && errno == ENOENT);
+static void sigiter(int sig, siginfo_t *info, void *arg)
+{
+ *batch = MI_BATCH_BUFFER_END;
+ __sync_synchronize();
}
-int drm_fd;
+#define MSEC_PER_SEC (1000)
+#define USEC_PER_SEC (1000 * MSEC_PER_SEC)
+#define NSEC_PER_SEC (1000 * USEC_PER_SEC)
+
+#define BUSY 1
+#define HANG 2
+static void basic(int fd, unsigned engine, unsigned flags)
+{
+ const int gen = intel_gen(intel_get_drm_devid(fd));
+ struct drm_i915_gem_exec_object2 obj;
+ struct drm_i915_gem_relocation_entry reloc;
+ struct drm_i915_gem_execbuffer2 execbuf;
+ struct drm_i915_gem_wait wait;
+ unsigned engines[16];
+ unsigned nengine;
+ int i, timeout;
+
+ nengine = 0;
+ if (engine == -1) {
+ for_each_engine(fd, engine)
+ if (engine) engines[nengine++] = engine;
+ } else {
+ igt_require(gem_has_ring(fd, engine));
+ engines[nengine++] = engine;
+ }
+ igt_require(nengine);
+
+ memset(&execbuf, 0, sizeof(execbuf));
+ execbuf.buffers_ptr = (uintptr_t)&obj;
+ execbuf.buffer_count = 1;
+
+ memset(&obj, 0, sizeof(obj));
+ obj.handle = gem_create(fd, 4096);
+
+ obj.relocs_ptr = (uintptr_t)&reloc;
+ obj.relocation_count = 1;
+ memset(&reloc, 0, sizeof(reloc));
+
+ batch = gem_mmap__gtt(fd, obj.handle, 4096, PROT_WRITE);
+ gem_set_domain(fd, obj.handle,
+ I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
+
+ reloc.target_handle = obj.handle; /* recurse */
+ reloc.presumed_offset = 0;
+ reloc.offset = sizeof(uint32_t);
+ reloc.delta = 0;
+ reloc.read_domains = I915_GEM_DOMAIN_COMMAND;
+ reloc.write_domain = 0;
+
+ i = 0;
+ batch[i] = MI_BATCH_BUFFER_START;
+ if (gen >= 8) {
+ batch[i] |= 1 << 8 | 1;
+ batch[++i] = 0;
+ batch[++i] = 0;
+ } else if (gen >= 6) {
+ batch[i] |= 1 << 8;
+ batch[++i] = 0;
+ } else {
+ batch[i] |= 2 << 6;
+ batch[++i] = 0;
+ if (gen < 4) {
+ batch[i] |= 1;
+ reloc.delta = 1;
+ }
+ }
+
+ for (i = 0; i < nengine; i++) {
+ execbuf.flags &= ~ENGINE_MASK;
+ execbuf.flags |= engines[i];
+ gem_execbuf(fd, &execbuf);
+ }
+
+ memset(&wait, 0, sizeof(wait));
+ wait.bo_handle = obj.handle;
+ igt_assert_eq(__gem_wait(fd, &wait), -ETIME);
+
+ if (flags & BUSY) {
+ struct timespec tv;
+
+ timeout = 120;
+ if ((flags & HANG) == 0) {
+ *batch = MI_BATCH_BUFFER_END;
+ __sync_synchronize();
+ timeout = 1;
+ }
+ munmap(batch, 4096);
+
+ memset(&tv, 0, sizeof(tv));
+ while (__gem_wait(fd, &wait) == -ETIME)
+ igt_assert(igt_seconds_elapsed(&tv) < timeout);
+ } else {
+ timer_t timer;
+
+ if ((flags & HANG) == 0) {
+ struct sigevent sev;
+ struct sigaction act;
+ struct itimerspec its;
+
+ memset(&sev, 0, sizeof(sev));
+ sev.sigev_notify = SIGEV_SIGNAL | SIGEV_THREAD_ID;
+ sev.sigev_notify_thread_id = gettid();
+ sev.sigev_signo = SIGRTMIN + 1;
+ igt_assert(timer_create(CLOCK_MONOTONIC, &sev, &timer) == 0);
+
+ memset(&act, 0, sizeof(act));
+ act.sa_sigaction = sigiter;
+ act.sa_flags = SA_SIGINFO;
+ igt_assert(sigaction(SIGRTMIN + 1, &act, NULL) == 0);
+
+ memset(&its, 0, sizeof(its));
+ its.it_value.tv_nsec = 0;
+ its.it_value.tv_sec = 1;
+ igt_assert(timer_settime(timer, 0, &its, NULL) == 0);
+ }
+
+ wait.timeout_ns = NSEC_PER_SEC / 2; /* 0.5s */
+ igt_assert_eq(__gem_wait(fd, &wait), -ETIME);
+ igt_assert_eq_s64(wait.timeout_ns, 0);
+
+ if ((flags & HANG) == 0) {
+ wait.timeout_ns = NSEC_PER_SEC; /* 1.0s */
+ igt_assert_eq(__gem_wait(fd, &wait), 0);
+ igt_assert(wait.timeout_ns > 0);
+ } else {
+ wait.timeout_ns = -1;
+ igt_assert_eq(__gem_wait(fd, &wait), 0);
+ igt_assert(wait.timeout_ns == -1);
+ }
+
+ wait.timeout_ns = 0;
+ igt_assert_eq(__gem_wait(fd, &wait), 0);
+ igt_assert(wait.timeout_ns == 0);
+
+ if ((flags & HANG) == 0)
+ timer_delete(timer);
+ }
+
+ gem_close(fd, obj.handle);
+}
igt_main
{
- igt_fixture
- drm_fd = drm_open_driver(DRIVER_INTEL);
+ const struct intel_execution_engine *e;
+ int fd = -1;
- igt_subtest("render_timeout")
- render_timeout(drm_fd);
+ igt_skip_on_simulation();
+
+ igt_fixture {
+ fd = drm_open_driver_master(DRIVER_INTEL);
+ }
igt_subtest("invalid-flags")
- invalid_flags(drm_fd);
+ invalid_flags(fd);
igt_subtest("invalid-buf")
- invalid_buf(drm_fd);
-
- igt_fixture
- close(drm_fd);
+ invalid_buf(fd);
+
+ igt_subtest_group {
+ igt_fixture {
+ igt_fork_hang_detector(fd);
+ igt_fork_signal_helper();
+ }
+
+ igt_subtest("basic-busy-all") {
+ gem_quiescent_gpu(fd);
+ basic(fd, -1, BUSY);
+ }
+ igt_subtest("basic-wait-all") {
+ gem_quiescent_gpu(fd);
+ basic(fd, -1, 0);
+ }
+
+ for (e = intel_execution_engines; e->name; e++) {
+ igt_subtest_group {
+ igt_subtest_f("busy-%s", e->name) {
+ gem_quiescent_gpu(fd);
+ basic(fd, e->exec_id | e->flags, BUSY);
+ }
+ igt_subtest_f("wait-%s", e->name) {
+ gem_quiescent_gpu(fd);
+ basic(fd, e->exec_id | e->flags, 0);
+ }
+ }
+ }
+
+ igt_fixture {
+ igt_stop_signal_helper();
+ igt_stop_hang_detector();
+ }
+ }
+
+ igt_subtest_group {
+ igt_hang_t hang;
+
+ igt_fixture {
+ hang = igt_allow_hang(fd, 0, 0);
+ igt_fork_signal_helper();
+ }
+
+ igt_subtest("hang-busy-all") {
+ gem_quiescent_gpu(fd);
+ basic(fd, -1, BUSY | HANG);
+ }
+ igt_subtest("hang-wait-all") {
+ gem_quiescent_gpu(fd);
+ basic(fd, -1, HANG);
+ }
+
+ for (e = intel_execution_engines; e->name; e++) {
+ igt_subtest_f("hang-busy-%s", e->name) {
+ gem_quiescent_gpu(fd);
+ basic(fd, e->exec_id | e->flags, HANG | BUSY);
+ }
+ igt_subtest_f("hang-wait-%s", e->name) {
+ gem_quiescent_gpu(fd);
+ basic(fd, e->exec_id | e->flags, HANG);
+ }
+ }
+
+ igt_fixture {
+ igt_stop_signal_helper();
+ igt_disallow_hang(fd, hang);
+ }
+ }
+
+ igt_fixture {
+ close(fd);
+ }
}