All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH igt] igt/gem_wait: Use explicit timers
@ 2016-10-12 15:22 Chris Wilson
  0 siblings, 0 replies; only message in thread
From: Chris Wilson @ 2016-10-12 15:22 UTC (permalink / raw)
  To: intel-gfx

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>
---
 lib/igt_core.h   |  27 ++++
 tests/gem_wait.c | 443 +++++++++++++++++++++++++++++++------------------------
 2 files changed, 277 insertions(+), 193 deletions(-)

diff --git a/lib/igt_core.h b/lib/igt_core.h
index 433b88c..03be757 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 461efdb..0ecb92f 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);
+	}
 }
-- 
2.9.3

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2016-10-12 15:22 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-10-12 15:22 [PATCH igt] igt/gem_wait: Use explicit timers Chris Wilson

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.