Skip to contentSkip to conversation

lib/dummyload: Add pollable spin batch

PATCH In review Tvrtko Ursulin 5 participants 14 messages Mar 22, 2018 – Mar 23, 2018 Library code

lore
Share & tools
lore
#0 lore raw

PATCH 1/3 lib/dummyload: Add pollable spin batch

From: Tvrtko Ursulin <[email protected]>

Pollable spin batch exports a spin->running pointer which can be checked by dereferencing it to see if the spinner is actually executing on the GPU.

This is useful for tests which want to make sure they do not proceed with their next step whilst the spinner is potentially only being processed by the driver and not actually executing.

Pollable spinner can be created with igt_spin_batch_new_poll or __igt_spin_batch_new_poll, after which a loop similar to:

	while (!*(volatile bool *)spin->running)
		;

Can be used to wait until the spinner start execution.

---
 lib/igt_dummyload.c  | 192 ++++++++++++++++++++++++++++++++++++++++-----------
 lib/igt_dummyload.h  |  11 +++
 lib/igt_gt.c         |   2 +-
 lib/ioctl_wrappers.c |   2 +-
 lib/ioctl_wrappers.h |   1 +
 5 files changed, 167 insertions(+), 41 deletions(-)
lib/igt_dummyload.c +153 -39
index dbc92e8f2951..98ab7ac2c6e9 100644
@@ -74,35 +74,48 @@ fill_reloc(struct drm_i915_gem_relocation_entry *reloc,
 	reloc->write_domain = write_domains;
 }
 
-static int emit_recursive_batch(igt_spin_t *spin,
-				int fd, uint32_t ctx, unsigned engine,
-				uint32_t dep, bool out_fence)
+#define OUT_FENCE	(1 << 0)
+#define POLL_RUN	(1 << 1)
+
+static int
+emit_recursive_batch(igt_spin_t *spin, int fd, uint32_t ctx, unsigned engine,
+		     uint32_t dep, unsigned int flags)
 {
 #define SCRATCH 0
 #define BATCH 1
 	const int gen = intel_gen(intel_get_drm_devid(fd));
-	struct drm_i915_gem_exec_object2 obj[2];
-	struct drm_i915_gem_relocation_entry relocs[2];
-	struct drm_i915_gem_execbuffer2 execbuf;
+	struct drm_i915_gem_relocation_entry relocs[2], *r;
+	struct drm_i915_gem_execbuffer2 *execbuf;
+	struct drm_i915_gem_exec_object2 *obj;
 	unsigned int engines[16];
 	unsigned int nengine;
 	int fence_fd = -1;
-	uint32_t *batch;
+	uint32_t *batch, *batch_start;
 	int i;
 
 	nengine = 0;
 	if (engine == ALL_ENGINES) {
-		for_each_engine(fd, engine)
-			if (engine)
+		for_each_engine(fd, engine) {
+			if (engine) {
+			if (flags & POLL_RUN)
+				igt_require(!(flags & POLL_RUN) ||
+					    gem_can_store_dword(fd, engine));
+
 				engines[nengine++] = engine;
+			}
+		}
 	} else {
 		gem_require_ring(fd, engine);
+		igt_require(!(flags & POLL_RUN) ||
+			    gem_can_store_dword(fd, engine));
 		engines[nengine++] = engine;
 	}
 	igt_require(nengine);
 
-	memset(&execbuf, 0, sizeof(execbuf));
-	memset(obj, 0, sizeof(obj));
+	memset(&spin->execbuf, 0, sizeof(spin->execbuf));
+	execbuf = &spin->execbuf;
+	memset(spin->obj, 0, sizeof(spin->obj));
+	obj = spin->obj;
 	memset(relocs, 0, sizeof(relocs));
 
 	obj[BATCH].handle = gem_create(fd, BATCH_SIZE);
@@ -113,19 +126,66 @@ static int emit_recursive_batch(igt_spin_t *spin,
 				       	BATCH_SIZE, PROT_WRITE);
 	gem_set_domain(fd, obj[BATCH].handle,
 			I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
-	execbuf.buffer_count++;
+	execbuf->buffer_count++;
+	batch_start = batch;
 
 	if (dep) {
+		igt_assert(!(flags & POLL_RUN));
+
 		/* dummy write to dependency */
 		obj[SCRATCH].handle = dep;
 		fill_reloc(&relocs[obj[BATCH].relocation_count++],
 			   dep, 1020,
 			   I915_GEM_DOMAIN_RENDER,
 			   I915_GEM_DOMAIN_RENDER);
-		execbuf.buffer_count++;
+		execbuf->buffer_count++;
+	} else if (flags & POLL_RUN) {
+		unsigned int offset;
+
+		igt_assert(!dep);
+
+		if (gen == 4 || gen == 5)
+			execbuf->flags |= I915_EXEC_SECURE;
+
+		spin->poll_handle = gem_create(fd, 4096);
+
+		if (__gem_set_caching(fd, spin->poll_handle,
+				      I915_CACHING_CACHED) == 0)
+			spin->running = __gem_mmap__cpu(fd, spin->poll_handle,
+							0, 4096,
+							PROT_READ | PROT_WRITE);
+		else
+			spin->running = __gem_mmap__wc(fd, spin->poll_handle,
+						       0, 4096,
+						       PROT_READ | PROT_WRITE);
+		igt_assert(spin->running);
+		igt_assert_eq(*spin->running, 0);
+
+		*batch++ = MI_STORE_DWORD_IMM | (gen < 6 ? 1 << 22 : 0);
+
+		if (gen >= 8) {
+			offset = 1;
+			*batch++ = 0;
+			*batch++ = 0;
+		} else if (gen >= 4) {
+			offset = 2;
+			*batch++ = 0;
+			*batch++ = 0;
+		} else {
+			offset = 1;
+			batch[-1]--;
+			*batch++ = 0;
+		}
+
+		*batch++ = 1;
+
+		obj[SCRATCH].handle = spin->poll_handle;
+		fill_reloc(&relocs[obj[BATCH].relocation_count++],
+			   spin->poll_handle, offset, 0, 0);
+		execbuf->buffer_count++;
 	}
 
-	spin->batch = batch;
+	spin->batch = batch = batch_start + 64 / sizeof(*batch);
 	spin->handle = obj[BATCH].handle;
 
 	/* Allow ourselves to be preempted */
@@ -145,40 +205,42 @@ static int emit_recursive_batch(igt_spin_t *spin,
 	batch += 1000;
 
 	/* recurse */
-	fill_reloc(&relocs[obj[BATCH].relocation_count],
-		   obj[BATCH].handle, (batch - spin->batch) + 1,
-		   I915_GEM_DOMAIN_COMMAND, 0);
+	r = &relocs[obj[BATCH].relocation_count++];
+	r->target_handle = obj[BATCH].handle;
+	r->offset = (batch + 1 - batch_start) * sizeof(*batch);
+	r->read_domains = I915_GEM_DOMAIN_COMMAND;
+	r->delta = 64;
 	if (gen >= 8) {
 		*batch++ = MI_BATCH_BUFFER_START | 1 << 8 | 1;
-		*batch++ = 0;
+		*batch++ = r->delta;
 		*batch++ = 0;
 	} else if (gen >= 6) {
 		*batch++ = MI_BATCH_BUFFER_START | 1 << 8;
-		*batch++ = 0;
+		*batch++ = r->delta;
 	} else {
 		*batch++ = MI_BATCH_BUFFER_START | 2 << 6;
-		*batch = 0;
-		if (gen < 4) {
-			*batch |= 1;
-			relocs[obj[BATCH].relocation_count].delta = 1;
-		}
+		if (gen < 4)
+			r->delta |= 1;
+		*batch = r->delta;
 		batch++;
 	}
-	obj[BATCH].relocation_count++;
 	obj[BATCH].relocs_ptr = to_user_pointer(relocs);
 
-	execbuf.buffers_ptr = to_user_pointer(obj + (2 - execbuf.buffer_count));
-	execbuf.rsvd1 = ctx;
+	execbuf->buffers_ptr = to_user_pointer(obj +
+					       (2 - execbuf->buffer_count));
+	execbuf->rsvd1 = ctx;
 
-	if (out_fence)
-		execbuf.flags |= I915_EXEC_FENCE_OUT;
+	if (flags & OUT_FENCE)
+		execbuf->flags |= I915_EXEC_FENCE_OUT;
 
 	for (i = 0; i < nengine; i++) {
-		execbuf.flags &= ~ENGINE_MASK;
-		execbuf.flags |= engines[i];
-		gem_execbuf_wr(fd, &execbuf);
-		if (out_fence) {
-			int _fd = execbuf.rsvd2 >> 32;
+		execbuf->flags &= ~ENGINE_MASK;
+		execbuf->flags |= engines[i];
+
+		gem_execbuf_wr(fd, execbuf);
+
+		if (flags & OUT_FENCE) {
+			int _fd = execbuf->rsvd2 >> 32;
 
 			igt_assert(_fd >= 0);
 			if (fence_fd == -1) {
@@ -194,12 +256,20 @@ static int emit_recursive_batch(igt_spin_t *spin,
 		}
 	}
 
+	/* Make it easier for callers to resubmit. */
+
+	obj[BATCH].relocation_count = 0;
+	obj[BATCH].relocs_ptr = 0;
+
+	obj[SCRATCH].flags = EXEC_OBJECT_PINNED;
+	obj[BATCH].flags = EXEC_OBJECT_PINNED;
+
 	return fence_fd;
 }
 
 static igt_spin_t *
 ___igt_spin_batch_new(int fd, uint32_t ctx, unsigned engine, uint32_t dep,
-		      int out_fence)
+		      unsigned int flags)
 {
 	igt_spin_t *spin;
 
@@ -207,7 +277,7 @@ ___igt_spin_batch_new(int fd, uint32_t ctx, unsigned engine, uint32_t dep,
 	igt_assert(spin);
 
 	spin->out_fence = emit_recursive_batch(spin, fd, ctx, engine, dep,
-					       out_fence);
+					       flags);
 
 	pthread_mutex_lock(&list_lock);
 	igt_list_add(&spin->link, &spin_list);
@@ -219,7 +289,7 @@ ___igt_spin_batch_new(int fd, uint32_t ctx, unsigned engine, uint32_t dep,
 igt_spin_t *
 __igt_spin_batch_new(int fd, uint32_t ctx, unsigned engine, uint32_t dep)
 {
-	return ___igt_spin_batch_new(fd, ctx, engine, dep, false);
+	return ___igt_spin_batch_new(fd, ctx, engine, dep, 0);
 }
 
 /**
@@ -253,7 +323,7 @@ igt_spin_batch_new(int fd, uint32_t ctx, unsigned engine, uint32_t dep)
 igt_spin_t *
 __igt_spin_batch_new_fence(int fd, uint32_t ctx, unsigned engine)
 {
-	return ___igt_spin_batch_new(fd, ctx, engine, 0, true);
+	return ___igt_spin_batch_new(fd, ctx, engine, 0, OUT_FENCE);
 }
 
 /**
@@ -286,6 +356,42 @@ igt_spin_batch_new_fence(int fd, uint32_t ctx, unsigned engine)
 	return spin;
 }
 
+igt_spin_t *
+__igt_spin_batch_new_poll(int fd, uint32_t ctx, unsigned engine)
+{
+	return ___igt_spin_batch_new(fd, ctx, engine, 0, POLL_RUN);
+}
+
+/**
+ * igt_spin_batch_new_poll:
+ * @fd: open i915 drm file descriptor
+ * @engine: Ring to execute batch OR'd with execbuf flags. If value is less
+ *          than 0, execute on all available rings.
+ *
+ * Start a recursive batch on a ring. Immediately returns a #igt_spin_t that
+ * contains the batch's handle that can be waited upon. The returned structure
+ * must be passed to igt_spin_batch_free() for post-processing.
+ *
+ * igt_spin_t->running will containt a pointer which target will change from
+ * zero to one once the spinner actually starts executing on the GPU.
+ *
+ * Returns:
+ * Structure with helper internal state for igt_spin_batch_free().
+ */
+igt_spin_t *
+igt_spin_batch_new_poll(int fd, uint32_t ctx, unsigned engine)
+{
+	igt_spin_t *spin;
+
+	igt_require_gem(fd);
+	igt_require(gem_mmap__has_wc(fd));
+
+	spin = __igt_spin_batch_new_poll(fd, ctx, engine);
+	igt_assert(gem_bo_busy(fd, spin->handle));
+
+	return spin;
+}
+
 static void notify(union sigval arg)
 {
 	igt_spin_t *spin = arg.sival_ptr;
@@ -340,6 +446,8 @@ void igt_spin_batch_end(igt_spin_t *spin)
 	if (!spin)
 		return;
 
+	igt_assert(*spin->batch == MI_ARB_CHK ||
+		   *spin->batch == MI_BATCH_BUFFER_END);
 	*spin->batch = MI_BATCH_BUFFER_END;
 	__sync_synchronize();
 }
@@ -365,7 +473,13 @@ void igt_spin_batch_free(int fd, igt_spin_t *spin)
 		timer_delete(spin->timer);
 
 	igt_spin_batch_end(spin);
-	gem_munmap(spin->batch, BATCH_SIZE);
+	gem_munmap((void *)((unsigned long)spin->batch & (~4095UL)),
+		   BATCH_SIZE);
+
+	if (spin->running) {
+		gem_munmap(spin->running, 4096);
+		gem_close(fd, spin->poll_handle);
+	}
 
 	gem_close(fd, spin->handle);
 
lib/igt_dummyload.h +11 -0
index 4103e4ab9e36..3103935a309b 100644
@@ -36,6 +36,10 @@ typedef struct igt_spin {
 	struct igt_list link;
 	uint32_t *batch;
 	int out_fence;
+	struct drm_i915_gem_exec_object2 obj[2];
+	struct drm_i915_gem_execbuffer2 execbuf;
+	uint32_t poll_handle;
+	bool *running;
 } igt_spin_t;
 
 igt_spin_t *__igt_spin_batch_new(int fd,
@@ -55,6 +59,13 @@ igt_spin_t *igt_spin_batch_new_fence(int fd,
 				     uint32_t ctx,
 				     unsigned engine);
 
+igt_spin_t *__igt_spin_batch_new_poll(int fd,
+				       uint32_t ctx,
+				       unsigned engine);
+igt_spin_t *igt_spin_batch_new_poll(int fd,
+				    uint32_t ctx,
+				    unsigned engine);
+
 void igt_spin_batch_set_timeout(igt_spin_t *spin, int64_t ns);
 void igt_spin_batch_end(igt_spin_t *spin);
 void igt_spin_batch_free(int fd, igt_spin_t *spin);
lib/igt_gt.c +1 -1
index 01aebc670862..4569fd36bd85 100644
@@ -609,7 +609,7 @@ bool gem_can_store_dword(int fd, unsigned int engine)
 	if (gen == 3 && (info->is_grantsdale || info->is_alviso))
 		return false; /* only supports physical addresses */
 
-	if (gen == 6 && (engine & ~(3<<13)) == I915_EXEC_BSD)
+	if (gen == 6 && ((engine & 0x3f) == I915_EXEC_BSD))
 		return false; /* kills the machine! */
 
 	if (info->is_broadwater)
lib/ioctl_wrappers.c +1 -1
index 8748cfcfc04f..4e1a08bf06b4 100644
@@ -198,7 +198,7 @@ void gem_set_tiling(int fd, uint32_t handle, uint32_t tiling, uint32_t stride)
 	igt_assert(__gem_set_tiling(fd, handle, tiling, stride) == 0);
 }
 
-static int __gem_set_caching(int fd, uint32_t handle, uint32_t caching)
+int __gem_set_caching(int fd, uint32_t handle, uint32_t caching)
 {
 	struct drm_i915_gem_caching arg;
 	int err;
lib/ioctl_wrappers.h +1 -0
index 13fbe3c103c0..b966f72c90a8 100644
@@ -61,6 +61,7 @@ bool gem_get_tiling(int fd, uint32_t handle, uint32_t *tiling, uint32_t *swizzle
 void gem_set_tiling(int fd, uint32_t handle, uint32_t tiling, uint32_t stride);
 int __gem_set_tiling(int fd, uint32_t handle, uint32_t tiling, uint32_t stride);
 
+int __gem_set_caching(int fd, uint32_t handle, uint32_t caching);
 void gem_set_caching(int fd, uint32_t handle, uint32_t caching);
 uint32_t gem_get_caching(int fd, uint32_t handle);
 uint32_t gem_flink(int fd, uint32_t handle);
Signature · 6 lines
2.14.1

_______________________________________________
Intel-gfx mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
#1 lore raw

PATCH 2/3 tests/gem_eio: Speed up test execution

From: Tvrtko Ursulin <[email protected]>

If we stop relying on regular GPU hangs to be detected, but trigger them manually as soon as we know our batch of interest is actually executing on the GPU, we can dramatically speed up various subtests.

This is enabled by the pollable spin batch added in the previous patch.

---
Note that the 'wait' subtest is mysteriously hanging for me in the no-op batch send by gem_test_engines, but only on RCS engine. TBD while I am getting some CI results.

---
 lib.tar         | Bin 0 -> 102400 bytes
 tests/gem_eio.c |  97 ++++++++++++++++++++++++++++++++++++++++----------------
 2 files changed, 70 insertions(+), 27 deletions(-)
 create mode 100644 lib.tar
lib.tar new file binary +0 -0
new file mode 100644
index 0000000000000000000000000000000000000000..ea04fad219a87f2e975852989526f8da4c9b7d6d
GIT binary patch
tests/gem_eio.c +70 -27

201 lines not on this page · raw message

Signature · 6 lines
2.14.1

_______________________________________________
Intel-gfx mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
#2 lore raw

PATCH v2 2/3 tests/gem_eio: Speed up test execution

From: Tvrtko Ursulin <[email protected]>

If we stop relying on regular GPU hangs to be detected, but trigger them manually as soon as we know our batch of interest is actually executing on the GPU, we can dramatically speed up various subtests.

This is enabled by the pollable spin batch added in the previous patch.

---
Note that the 'wait' subtest is mysteriously hanging for me in the no-op batch send by gem_test_engines, but only on RCS engine. TBD while I am getting some CI results.

---
 tests/gem_eio.c | 97 +++++++++++++++++++++++++++++++++++++++++----------------
 1 file changed, 70 insertions(+), 27 deletions(-)
tests/gem_eio.c +70 -27
index 4bcc5937db39..93400056124b 100644
@@ -71,26 +71,23 @@ static void trigger_reset(int fd)
 	gem_quiescent_gpu(fd);
 }
 
-static void wedge_gpu(int fd)
+static void manual_hang(int drm_fd)
 {
-	/* First idle the GPU then disable GPU resets before injecting a hang */
-	gem_quiescent_gpu(fd);
-
-	igt_require(i915_reset_control(false));
+	int dir = igt_debugfs_dir(drm_fd);
 
-	igt_debug("Wedging GPU by injecting hang\n");
-	igt_post_hang_ring(fd, igt_hang_ring(fd, I915_EXEC_DEFAULT));
+	igt_sysfs_set(dir, "i915_wedged", "-1");
 
-	igt_assert(i915_reset_control(true));
+	close(dir);
 }
 
-static void wedgeme(int drm_fd)
+static void wedge_gpu(int fd)
 {
-	int dir = igt_debugfs_dir(drm_fd);
-
-	igt_sysfs_set(dir, "i915_wedged", "-1");
+	/* First idle the GPU then disable GPU resets before injecting a hang */
+	gem_quiescent_gpu(fd);
 
-	close(dir);
+	igt_require(i915_reset_control(false));
+	manual_hang(fd);
+	igt_assert(i915_reset_control(true));
 }
 
 static int __gem_throttle(int fd)
@@ -149,29 +146,66 @@ static int __gem_wait(int fd, uint32_t handle, int64_t timeout)
 	return err;
 }
 
+static igt_spin_t * __spin_poll(int fd, uint32_t ctx, unsigned long flags)
+{
+	if (gem_can_store_dword(fd, flags))
+		return __igt_spin_batch_new_poll(fd, ctx, flags);
+	else
+		return __igt_spin_batch_new(fd, ctx, flags, 0);
+}
+
+static void __spin_wait(int fd, igt_spin_t *spin)
+{
+	if (spin->running) {
+		while (!*((volatile bool *)spin->running))
+			;
+	} else {
+		igt_debug("__spin_wait - usleep mode\n");
+		usleep(500e3); /* Better than nothing! */
+	}
+}
+
+/*
+ * Wedge the GPU when we know our batch is running.
+ */
+static void wedge_after_running(int fd, igt_spin_t *spin)
+{
+	__spin_wait(fd, spin);
+	manual_hang(fd);
+}
+
 static void test_wait(int fd)
 {
-	igt_hang_t hang;
+	struct timespec ts = { };
+	igt_spin_t *hang;
 
 	igt_require_gem(fd);
 
+	igt_nsec_elapsed(&ts);
+
 	/* If the request we wait on completes due to a hang (even for
 	 * that request), the user expects the return value to 0 (success).
 	 */
-	hang = igt_hang_ring(fd, I915_EXEC_DEFAULT);
-	igt_assert_eq(__gem_wait(fd, hang.handle, -1), 0);
-	igt_post_hang_ring(fd, hang);
+	igt_require(i915_reset_control(true));
+	hang = __spin_poll(fd, 0, I915_EXEC_DEFAULT);
+	wedge_after_running(fd, hang);
+	igt_assert_eq(__gem_wait(fd, hang->handle, -1), 0);
+	igt_spin_batch_free(fd, hang);
 
 	/* If the GPU is wedged during the wait, again we expect the return
 	 * value to be 0 (success).
 	 */
 	igt_require(i915_reset_control(false));
-	hang = igt_hang_ring(fd, I915_EXEC_DEFAULT);
-	igt_assert_eq(__gem_wait(fd, hang.handle, -1), 0);
-	igt_post_hang_ring(fd, hang);
+	hang = __spin_poll(fd, 0, I915_EXEC_DEFAULT);
+	wedge_after_running(fd, hang);
+	igt_assert_eq(__gem_wait(fd, hang->handle, -1), 0);
+	igt_spin_batch_free(fd, hang);
 	igt_require(i915_reset_control(true));
 
 	trigger_reset(fd);
+
+	/* HACK for CI */
+	igt_assert(igt_nsec_elapsed(&ts) < 5e9);
 }
 
 static void test_suspend(int fd, int state)
@@ -181,7 +215,7 @@ static void test_suspend(int fd, int state)
 
 	/* Check we can suspend when the driver is already wedged */
 	igt_require(i915_reset_control(false));
-	wedgeme(fd);
+	manual_hang(fd);
 
 	igt_system_suspend_autoresume(state, SUSPEND_TEST_DEVICES);
 
@@ -209,11 +243,10 @@ static void test_inflight(int fd)
 		int fence[64]; /* conservative estimate of ring size */
 
 		gem_quiescent_gpu(fd);
-
 		igt_debug("Starting %s on engine '%s'\n", __func__, e__->name);
 		igt_require(i915_reset_control(false));
 
-		hang = __igt_spin_batch_new(fd, 0, engine, 0);
+		hang = __spin_poll(fd, 0, engine);
 		obj[0].handle = hang->handle;
 
 		memset(&execbuf, 0, sizeof(execbuf));
@@ -227,6 +260,8 @@ static void test_inflight(int fd)
 			igt_assert(fence[n] != -1);
 		}
 
+		wedge_after_running(fd, hang);
+
 		igt_assert_eq(__gem_wait(fd, obj[1].handle, -1), 0);
 		for (unsigned int n = 0; n < ARRAY_SIZE(fence); n++) {
 			igt_assert_eq(sync_fence_status(fence[n]), -EIO);
@@ -256,7 +291,7 @@ static void test_inflight_suspend(int fd)
 	obj[1].handle = gem_create(fd, 4096);
 	gem_write(fd, obj[1].handle, 0, &bbe, sizeof(bbe));
 
-	hang = __igt_spin_batch_new(fd, 0, 0, 0);
+	hang = __spin_poll(fd, 0, 0);
 	obj[0].handle = hang->handle;
 
 	memset(&execbuf, 0, sizeof(execbuf));
@@ -273,6 +308,8 @@ static void test_inflight_suspend(int fd)
 	igt_set_autoresume_delay(30);
 	igt_system_suspend_autoresume(SUSPEND_STATE_MEM, SUSPEND_TEST_NONE);
 
+	wedge_after_running(fd, hang);
+
 	igt_assert_eq(__gem_wait(fd, obj[1].handle, -1), 0);
 	for (unsigned int n = 0; n < ARRAY_SIZE(fence); n++) {
 		igt_assert_eq(sync_fence_status(fence[n]), -EIO);
@@ -330,7 +367,7 @@ static void test_inflight_contexts(int fd)
 		igt_debug("Starting %s on engine '%s'\n", __func__, e__->name);
 		igt_require(i915_reset_control(false));
 
-		hang = __igt_spin_batch_new(fd, 0, engine, 0);
+		hang = __spin_poll(fd, ctx[0], engine);
 		obj[0].handle = hang->handle;
 
 		memset(&execbuf, 0, sizeof(execbuf));
@@ -345,6 +382,8 @@ static void test_inflight_contexts(int fd)
 			igt_assert(fence[n] != -1);
 		}
 
+		wedge_after_running(fd, hang);
+
 		igt_assert_eq(__gem_wait(fd, obj[1].handle, -1), 0);
 		for (unsigned int n = 0; n < ARRAY_SIZE(fence); n++) {
 			igt_assert_eq(sync_fence_status(fence[n]), -EIO);
@@ -375,7 +414,7 @@ static void test_inflight_external(int fd)
 	fence = igt_cork_plug(&cork, fd);
 
 	igt_require(i915_reset_control(false));
-	hang = __igt_spin_batch_new(fd, 0, 0, 0);
+	hang = __spin_poll(fd, 0, 0);
 
 	memset(&obj, 0, sizeof(obj));
 	obj.handle = gem_create(fd, 4096);
@@ -393,6 +432,8 @@ static void test_inflight_external(int fd)
 	fence = execbuf.rsvd2 >> 32;
 	igt_assert(fence != -1);
 
+	wedge_after_running(fd, hang);
+
 	gem_sync(fd, hang->handle); /* wedged, with an unready batch */
 	igt_assert(!gem_bo_busy(fd, hang->handle));
 	igt_assert(gem_bo_busy(fd, obj.handle));
@@ -420,7 +461,7 @@ static void test_inflight_internal(int fd)
 	igt_require(gem_has_exec_fence(fd));
 
 	igt_require(i915_reset_control(false));
-	hang = __igt_spin_batch_new(fd, 0, 0, 0);
+	hang = __spin_poll(fd, 0, 0);
 
 	memset(obj, 0, sizeof(obj));
 	obj[0].handle = hang->handle;
@@ -441,6 +482,8 @@ static void test_inflight_internal(int fd)
 		nfence++;
 	}
 
+	wedge_after_running(fd, hang);
+
 	igt_assert_eq(__gem_wait(fd, obj[1].handle, -1), 0);
 	while (nfence--) {
 		igt_assert_eq(sync_fence_status(fences[nfence]), -EIO);
Signature · 6 lines
2.14.1

_______________________________________________
igt-dev mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/igt-dev
#3 lore raw

Re: [igt-dev] [PATCH i-g-t 2/3] tests/gem_eio: Speed up test execution

In reply to Tvrtko Ursulin

Quoting Tvrtko Ursulin (2018-03-22 11:17:11)

lib.tar

Quoted 27 lines

From: Tvrtko Ursulin <[email protected]>

If we stop relying on regular GPU hangs to be detected, but trigger them manually as soon as we know our batch of interest is actually executing on the GPU, we can dramatically speed up various subtests.

This is enabled by the pollable spin batch added in the previous patch.

---
Note that the 'wait' subtest is mysteriously hanging for me in the no-op batch send by gem_test_engines, but only on RCS engine. TBD while I am getting some CI results.

---
 lib.tar         | Bin 0 -> 102400 bytes
 tests/gem_eio.c |  97 ++++++++++++++++++++++++++++++++++++++++----------------
 2 files changed, 70 insertions(+), 27 deletions(-)
 create mode 100644 lib.tar
lib.tar new file binary +0 -0
new file mode 100644
index 0000000000000000000000000000000000000000..ea04fad219a87f2e975852989526f8da4c9b7d6d
GIT binary patch

Looks correct.

Quoted 14 lines
tests/gem_eio.c +1 -4
index 4bcc5937db39..93400056124b 100644
@@ -71,26 +71,23 @@ static void trigger_reset(int fd)
        gem_quiescent_gpu(fd);
 }
 
-static void wedge_gpu(int fd)
+static void manual_hang(int drm_fd)
 {
-       /* First idle the GPU then disable GPU resets before injecting a hang */
-       gem_quiescent_gpu(fd);
-

tests/gem_eio.c static void trigger_reset(int fd) · line 81

Quoted 10 lines
tests/gem_eio.c +3 -4
-       igt_require(i915_reset_control(false));
+       int dir = igt_debugfs_dir(drm_fd);
 
-       igt_debug("Wedging GPU by injecting hang\n");
-       igt_post_hang_ring(fd, igt_hang_ring(fd, I915_EXEC_DEFAULT));
+       igt_sysfs_set(dir, "i915_wedged", "-1");
 
-       igt_assert(i915_reset_control(true));
+       close(dir);
 }

Ok.

Quoted 4 lines
tests/gem_eio.c +1 -2
-static void wedgeme(int drm_fd)
+static void wedge_gpu(int fd)
 {
-       int dir = igt_debugfs_dir(drm_fd);

tests/gem_eio.c static void trigger_reset(int fd) · line 91

Quoted 10 lines
tests/gem_eio.c +5 -3
-
-       igt_sysfs_set(dir, "i915_wedged", "-1");
+       /* First idle the GPU then disable GPU resets before injecting a hang */
+       gem_quiescent_gpu(fd);
 
-       close(dir);
+       igt_require(i915_reset_control(false));
+       manual_hang(fd);
+       igt_assert(i915_reset_control(true));
 }

Ok.

Quoted 59 lines
tests/gem_eio.c +37 -4
 static int __gem_throttle(int fd)
@@ -149,29 +146,66 @@ static int __gem_wait(int fd, uint32_t handle, int64_t timeout)
        return err;
 }
 
+static igt_spin_t * __spin_poll(int fd, uint32_t ctx, unsigned long flags)
+{
+       if (gem_can_store_dword(fd, flags))
+               return __igt_spin_batch_new_poll(fd, ctx, flags);
+       else
+               return __igt_spin_batch_new(fd, ctx, flags, 0);
+}
+
+static void __spin_wait(int fd, igt_spin_t *spin)
+{
+       if (spin->running) {
+               while (!*((volatile bool *)spin->running))
+                       ;
+       } else {
+               igt_debug("__spin_wait - usleep mode\n");
+               usleep(500e3); /* Better than nothing! */
+       }
+}
+
+/*
+ * Wedge the GPU when we know our batch is running.
+ */
+static void wedge_after_running(int fd, igt_spin_t *spin)
+{
+       __spin_wait(fd, spin);
+       manual_hang(fd);
+}
+
 static void test_wait(int fd)
 {
-       igt_hang_t hang;
+       struct timespec ts = { };
+       igt_spin_t *hang;
 
        igt_require_gem(fd);
 
+       igt_nsec_elapsed(&ts);
+
        /* If the request we wait on completes due to a hang (even for
         * that request), the user expects the return value to 0 (success).
         */
-       hang = igt_hang_ring(fd, I915_EXEC_DEFAULT);
-       igt_assert_eq(__gem_wait(fd, hang.handle, -1), 0);
-       igt_post_hang_ring(fd, hang);
+       igt_require(i915_reset_control(true));
+       hang = __spin_poll(fd, 0, I915_EXEC_DEFAULT);
+       wedge_after_running(fd, hang);
+       igt_assert_eq(__gem_wait(fd, hang->handle, -1), 0);
+       igt_spin_batch_free(fd, hang);
 
 
        /* If the GPU is wedged during the wait, again we expect the return
         * value to be 0 (success).

tests/gem_eio.c static int __gem_wait(int fd, uint32_t handle, int64_t timeout) · line 204

Quoted 10 lines
tests/gem_eio.c +4 -3
         */
        igt_require(i915_reset_control(false));
-       hang = igt_hang_ring(fd, I915_EXEC_DEFAULT);
-       igt_assert_eq(__gem_wait(fd, hang.handle, -1), 0);
-       igt_post_hang_ring(fd, hang);
+       hang = __spin_poll(fd, 0, I915_EXEC_DEFAULT);
+       wedge_after_running(fd, hang);
+       igt_assert_eq(__gem_wait(fd, hang->handle, -1), 0);
+       igt_spin_batch_free(fd, hang);
        igt_require(i915_reset_control(true));

Hmm. These are not equivalent to the original test. The tests requires hangcheck to kick in while the test is blocked on igt_wait. To do a fast equivalent, we need to kick off a timer. (Here we are just asking if a wait on an already completed request doesn't block, not how we handle the reset in the middle of a wait. Seems a reasonable addition though.)

I think that's a general pattern worth repeating for the rest of tests:
don't immediately inject the hang, but leave it a few milliseconds to allow us to block on the subsequent wait. I would even repeat the tests a few times with different timeouts; 0, 1us, 10ms (thinking of the different phases for i915_request_wait).

tests/gem_eio.c static int __gem_wait(int fd, uint32_t handle, int64_t timeout) · line 208

Quoted 4 lines
tests/gem_eio.c +3 -0
        trigger_reset(fd);
+
+       /* HACK for CI */
+       igt_assert(igt_nsec_elapsed(&ts) < 5e9);

igt_seconds_elapsed() the approximation is worth the readability.

In this case you might like to try igt_set_timeout(), as I think each subtest and exithandlers are in place to make them robust against premature failures.
-Chris
_______________________________________________
igt-dev mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/igt-dev

#4 lore raw

Re: [igt-dev] [PATCH i-g-t 2/3] tests/gem_eio: Speed up test execution

Quoted 22 lines · Chris Wilson

Quoting Tvrtko Ursulin (2018-03-22 11:17:11)

Quoted 21 lines

From: Tvrtko Ursulin <[email protected]>

If we stop relying on regular GPU hangs to be detected, but trigger them manually as soon as we know our batch of interest is actually executing on the GPU, we can dramatically speed up various subtests.

This is enabled by the pollable spin batch added in the previous patch.

---
Note that the 'wait' subtest is mysteriously hanging for me in the no-op batch send by gem_test_engines, but only on RCS engine. TBD while I am getting some CI results.

---
  lib.tar         | Bin 0 -> 102400 bytes
  tests/gem_eio.c |  97 ++++++++++++++++++++++++++++++++++++++++----------------
  2 files changed, 70 insertions(+), 27 deletions(-)

create mode 100644 lib.tar

Quoted 8 lines

lib.tar

Quoted 6 lines
lib.tar new file binary +0 -0
new file mode 100644
index 0000000000000000000000000000000000000000..ea04fad219a87f2e975852989526f8da4c9b7d6d
GIT binary patch

Looks correct.

Just backing up in the cloud. :))

Quoted 36 lines
Quoted 14 lines
tests/gem_eio.c +1 -4
index 4bcc5937db39..93400056124b 100644
@@ -71,26 +71,23 @@ static void trigger_reset(int fd)
         gem_quiescent_gpu(fd);
  }
 
-static void wedge_gpu(int fd)
+static void manual_hang(int drm_fd)
  {
-       /* First idle the GPU then disable GPU resets before injecting a hang */
-       gem_quiescent_gpu(fd);
-

tests/gem_eio.c static void trigger_reset(int fd) · line 81

Quoted 10 lines
tests/gem_eio.c +3 -4
-       igt_require(i915_reset_control(false));
+       int dir = igt_debugfs_dir(drm_fd);
 
-       igt_debug("Wedging GPU by injecting hang\n");
-       igt_post_hang_ring(fd, igt_hang_ring(fd, I915_EXEC_DEFAULT));
+       igt_sysfs_set(dir, "i915_wedged", "-1");
 
-       igt_assert(i915_reset_control(true));
+       close(dir);
  }

Ok.

Quoted 9 lines
tests/gem_eio.c +3 -4
-static void wedgeme(int drm_fd)
+static void wedge_gpu(int fd)
  {
-       int dir = igt_debugfs_dir(drm_fd);
-
-       igt_sysfs_set(dir, "i915_wedged", "-1");
+       /* First idle the GPU then disable GPU resets before injecting a hang */
+       gem_quiescent_gpu(fd);
 
Quoted 7 lines

tests/gem_eio.c static void trigger_reset(int fd) · line 91

Quoted 5 lines
tests/gem_eio.c +3 -1
-       close(dir);
+       igt_require(i915_reset_control(false));
+       manual_hang(fd);
+       igt_assert(i915_reset_control(true));
  }

Ok.

Well done reading that awful diff!

Quoted 77 lines
Quoted 59 lines
tests/gem_eio.c +37 -4
  static int __gem_throttle(int fd)
@@ -149,29 +146,66 @@ static int __gem_wait(int fd, uint32_t handle, int64_t timeout)
         return err;
  }
 
+static igt_spin_t * __spin_poll(int fd, uint32_t ctx, unsigned long flags)
+{
+       if (gem_can_store_dword(fd, flags))
+               return __igt_spin_batch_new_poll(fd, ctx, flags);
+       else
+               return __igt_spin_batch_new(fd, ctx, flags, 0);
+}
+
+static void __spin_wait(int fd, igt_spin_t *spin)
+{
+       if (spin->running) {
+               while (!*((volatile bool *)spin->running))
+                       ;
+       } else {
+               igt_debug("__spin_wait - usleep mode\n");
+               usleep(500e3); /* Better than nothing! */
+       }
+}
+
+/*
+ * Wedge the GPU when we know our batch is running.
+ */
+static void wedge_after_running(int fd, igt_spin_t *spin)
+{
+       __spin_wait(fd, spin);
+       manual_hang(fd);
+}
+
  static void test_wait(int fd)
  {
-       igt_hang_t hang;
+       struct timespec ts = { };
+       igt_spin_t *hang;
 
         igt_require_gem(fd);
 
+       igt_nsec_elapsed(&ts);
+
         /* If the request we wait on completes due to a hang (even for
          * that request), the user expects the return value to 0 (success).
          */
-       hang = igt_hang_ring(fd, I915_EXEC_DEFAULT);
-       igt_assert_eq(__gem_wait(fd, hang.handle, -1), 0);
-       igt_post_hang_ring(fd, hang);
+       igt_require(i915_reset_control(true));
+       hang = __spin_poll(fd, 0, I915_EXEC_DEFAULT);
+       wedge_after_running(fd, hang);
+       igt_assert_eq(__gem_wait(fd, hang->handle, -1), 0);
+       igt_spin_batch_free(fd, hang);
 
 
         /* If the GPU is wedged during the wait, again we expect the return
          * value to be 0 (success).

tests/gem_eio.c static int __gem_wait(int fd, uint32_t handle, int64_t timeout) · line 204

Quoted 10 lines
tests/gem_eio.c +4 -3
          */
         igt_require(i915_reset_control(false));
-       hang = igt_hang_ring(fd, I915_EXEC_DEFAULT);
-       igt_assert_eq(__gem_wait(fd, hang.handle, -1), 0);
-       igt_post_hang_ring(fd, hang);
+       hang = __spin_poll(fd, 0, I915_EXEC_DEFAULT);
+       wedge_after_running(fd, hang);
+       igt_assert_eq(__gem_wait(fd, hang->handle, -1), 0);
+       igt_spin_batch_free(fd, hang);
         igt_require(i915_reset_control(true));

Hmm. These are not equivalent to the original test. The tests requires hangcheck to kick in while the test is blocked on igt_wait. To do a fast equivalent, we need to kick off a timer. (Here we are just asking if a wait on an already completed request doesn't block, not how we handle the reset in the middle of a wait. Seems a reasonable addition though.)

Quoted 5 lines

I think that's a general pattern worth repeating for the rest of tests:
don't immediately inject the hang, but leave it a few milliseconds to allow us to block on the subsequent wait. I would even repeat the tests a few times with different timeouts; 0, 1us, 10ms (thinking of the different phases for i915_request_wait).

True, it's not the same.

Makes sense to test with different timeouts. Will do.

Quoted 11 lines

tests/gem_eio.c static int __gem_wait(int fd, uint32_t handle, int64_t timeout) · line 208

Quoted 5 lines
tests/gem_eio.c +3 -0
         trigger_reset(fd);
+
+       /* HACK for CI */
+       igt_assert(igt_nsec_elapsed(&ts) < 5e9);

igt_seconds_elapsed() the approximation is worth the readability.

In this case you might like to try igt_set_timeout(), as I think each subtest and exithandlers are in place to make them robust against premature failures.

Well this was just to see that will happen on the shards here. As mentioned in the commit I get that yet unexplained GPU hang at subtest exit here. So the assert above is just to notice if the same happens on shards.

Regards,

Tvrtko
_______________________________________________
igt-dev mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/igt-dev

#5 lore raw

Re: [igt-dev] [PATCH i-g-t 2/3] tests/gem_eio: Speed up test execution

Quoting Tvrtko Ursulin (2018-03-22 12:36:58)

Quoted 11 lines · Chris Wilson
Quoted 9 lines

Quoting Tvrtko Ursulin (2018-03-22 11:17:11)

tests/gem_eio.c static int __gem_wait(int fd, uint32_t handle, int64_t timeout) · line 208

Quoted 4 lines
         trigger_reset(fd);
tests/gem_eio.c +3 -0
+
+       /* HACK for CI */
+       igt_assert(igt_nsec_elapsed(&ts) < 5e9);

igt_seconds_elapsed() the approximation is worth the readability.

Quoted 8 lines
Quoted 3 lines

In this case you might like to try igt_set_timeout(), as I think each subtest and exithandlers are in place to make them robust against premature failures.

Well this was just to see that will happen on the shards here. As mentioned in the commit I get that yet unexplained GPU hang at subtest exit here. So the assert above is just to notice if the same happens on shards.

And I was thinking it was a reasonable enhancement :) Probably more so for igt/gem_wait itself to ask that if we reset the request we are waiting upon it completes in a timely manner. (We don't care about wedged handling there, just reset handling.)
-Chris
_______________________________________________
igt-dev mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/igt-dev

#6 lore raw

Re: [igt-dev] [Intel-gfx] [PATCH i-g-t 2/3] tests/gem_eio: Speed up test execution

Quoted 21 lines · Chris Wilson

Quoting Tvrtko Ursulin (2018-03-22 12:36:58)

Quoted 11 lines · Chris Wilson
Quoted 9 lines

Quoting Tvrtko Ursulin (2018-03-22 11:17:11)

tests/gem_eio.c static int __gem_wait(int fd, uint32_t handle, int64_t timeout) · line 208

Quoted 4 lines
          trigger_reset(fd);
tests/gem_eio.c +3 -0
+
+       /* HACK for CI */
+       igt_assert(igt_nsec_elapsed(&ts) < 5e9);

igt_seconds_elapsed() the approximation is worth the readability.

Quoted 8 lines
Quoted 3 lines

In this case you might like to try igt_set_timeout(), as I think each subtest and exithandlers are in place to make them robust against premature failures.

Well this was just to see that will happen on the shards here. As mentioned in the commit I get that yet unexplained GPU hang at subtest exit here. So the assert above is just to notice if the same happens on shards.

Quoted 4 lines

And I was thinking it was a reasonable enhancement :) Probably more so for igt/gem_wait itself to ask that if we reset the request we are waiting upon it completes in a timely manner. (We don't care about wedged handling there, just reset handling.)

How about checking for reset when we do gem_test_engine(), which seems to not fail on reset, (crudely https://paste.debian.net/1016059/)?

Antonio

Quoted 6 lines

-Chris
_______________________________________________
Intel-gfx mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

_______________________________________________
igt-dev mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/igt-dev

#7 lore raw

Re: [igt-dev] [Intel-gfx] [PATCH i-g-t 2/3] tests/gem_eio: Speed up test execution

Quoting Antonio Argenziano (2018-03-22 17:32:46)

Quoted 24 lines · Chris Wilson
Quoted 21 lines

Quoting Tvrtko Ursulin (2018-03-22 12:36:58)

Quoted 19 lines · Chris Wilson
Quoted 7 lines

Quoting Tvrtko Ursulin (2018-03-22 11:17:11)

tests/gem_eio.c static int __gem_wait(int fd, uint32_t handle, int64_t timeout) · line 208

Quoted 4 lines
          trigger_reset(fd);
tests/gem_eio.c +3 -0
+
+       /* HACK for CI */
+       igt_assert(igt_nsec_elapsed(&ts) < 5e9);
Quoted 5 lines

igt_seconds_elapsed() the approximation is worth the readability.

In this case you might like to try igt_set_timeout(), as I think each subtest and exithandlers are in place to make them robust against premature failures.

Well this was just to see that will happen on the shards here. As mentioned in the commit I get that yet unexplained GPU hang at subtest exit here. So the assert above is just to notice if the same happens on shards.

Quoted 7 lines
Quoted 4 lines

And I was thinking it was a reasonable enhancement :) Probably more so for igt/gem_wait itself to ask that if we reset the request we are waiting upon it completes in a timely manner. (We don't care about wedged handling there, just reset handling.)

How about checking for reset when we do gem_test_engine(), which seems to not fail on reset, (crudely https://paste.debian.net/1016059/)?

I was thinking that the timeout would be good around the test as a whole, because it is now meant to be uberfast.
-Chris
_______________________________________________
igt-dev mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/igt-dev

#8 lore raw

Re: [Intel-gfx] [igt-dev] [PATCH i-g-t 2/3] tests/gem_eio: Speed up test execution

Quoted 30 lines · Chris Wilson

Quoting Antonio Argenziano (2018-03-22 17:32:46)

Quoted 29 lines · Chris Wilson
Quoted 25 lines

Quoting Tvrtko Ursulin (2018-03-22 12:36:58)

Quoted 11 lines · Chris Wilson
Quoted 9 lines

Quoting Tvrtko Ursulin (2018-03-22 11:17:11)

tests/gem_eio.c static int __gem_wait(int fd, uint32_t handle, int64_t timeout) · line 208

Quoted 4 lines
           trigger_reset(fd);
tests/gem_eio.c +3 -0
+
+       /* HACK for CI */
+       igt_assert(igt_nsec_elapsed(&ts) < 5e9);

igt_seconds_elapsed() the approximation is worth the readability.

Quoted 8 lines
Quoted 3 lines

In this case you might like to try igt_set_timeout(), as I think each subtest and exithandlers are in place to make them robust against premature failures.

Well this was just to see that will happen on the shards here. As mentioned in the commit I get that yet unexplained GPU hang at subtest exit here. So the assert above is just to notice if the same happens on shards.

And I was thinking it was a reasonable enhancement :) Probably more so for igt/gem_wait itself to ask that if we reset the request we are waiting upon it completes in a timely manner. (We don't care about wedged handling there, just reset handling.)

Quoted 5 lines
Quoted 2 lines

How about checking for reset when we do gem_test_engine(), which seems to not fail on reset, (crudely https://paste.debian.net/1016059/)?

I was thinking that the timeout would be good around the test as a whole, because it is now meant to be uberfast.

Makes sense.

Thanks,
Antonio

Quoted 2 lines

-Chris

_______________________________________________
Intel-gfx mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

#9 lore raw

Re: [igt-dev] [Intel-gfx] [PATCH i-g-t 2/3] tests/gem_eio: Speed up test execution

In reply to Chris Wilson
Quoted 30 lines · Chris Wilson

Quoting Antonio Argenziano (2018-03-22 17:32:46)

Quoted 29 lines · Chris Wilson
Quoted 25 lines

Quoting Tvrtko Ursulin (2018-03-22 12:36:58)

Quoted 11 lines · Chris Wilson
Quoted 9 lines

Quoting Tvrtko Ursulin (2018-03-22 11:17:11)

tests/gem_eio.c static int __gem_wait(int fd, uint32_t handle, int64_t timeout) · line 208

Quoted 4 lines
           trigger_reset(fd);
tests/gem_eio.c +3 -0
+
+       /* HACK for CI */
+       igt_assert(igt_nsec_elapsed(&ts) < 5e9);

igt_seconds_elapsed() the approximation is worth the readability.

Quoted 8 lines
Quoted 3 lines

In this case you might like to try igt_set_timeout(), as I think each subtest and exithandlers are in place to make them robust against premature failures.

Well this was just to see that will happen on the shards here. As mentioned in the commit I get that yet unexplained GPU hang at subtest exit here. So the assert above is just to notice if the same happens on shards.

And I was thinking it was a reasonable enhancement :) Probably more so for igt/gem_wait itself to ask that if we reset the request we are waiting upon it completes in a timely manner. (We don't care about wedged handling there, just reset handling.)

Quoted 5 lines
Quoted 2 lines

How about checking for reset when we do gem_test_engine(), which seems to not fail on reset, (crudely https://paste.debian.net/1016059/)?

I was thinking that the timeout would be good around the test as a whole, because it is now meant to be uberfast.

Whole subtests with igt_set_timeout, or alternatively I was thinking to just time the trigger_reset helper. Since other bits are covered by short timeout gem_wait now, I think tigger_reset is the only one which I need to additionally cover. If we stick with signals for delayed hangs, that also might be simpler than to find a different signal than SIGALARM for it (so it doesn't clash with igt_set_timeout).

Regards,

Tvrtko

_______________________________________________
igt-dev mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/igt-dev

#10 lore raw

PATCH 3/3 tests/perf_pmu: Improve accuracy by waiting on spinner to start

In reply to Tvrtko Ursulin

From: Tvrtko Ursulin <[email protected]>

More than one test assumes that the spinner is running pretty much immediately after we have create or submitted it.

In actuality there is a variable delay, especially on execlists platforms, between submission and spin batch starting to run on the hardware.

To enable tests which care about this level of timing to account for this, we add a new spin batch constructor which provides an output field which can be polled to determine when the batch actually started running.

This is implemented via MI_STOREDW_IMM from the spin batch, writing into memory mapped page shared with userspace.

Using this facility from perf_pmu, where applicable, should improve very occasional test fails across the set and platforms.

v2:
Chris Wilson:
* Use caching mapping if available.
* Handle old gens better.
* Use gem_can_store_dword.
* Cache exec obj array in spin_batch_t for easier resubmit.

v3:
* Forgot I915_EXEC_NO_RELOC. (Chris Wilson)

v4:
* Mask out all non-engine flags in gem_can_store_dword.
* Added some debug logging.

v5:
* Fix relocs and batch munmap. (Chris)
* Added assert idle spinner batch looks as expected.

v6:
* Skip accuracy tests when !gem_can_store_dword.

v7:
* Fix batch recursion reloc address.

v8:
Chris Wilson:
* Pull up gem_can_store_dword check before we start submitting.
* Build spinner batch in a way we can skip store dword when not needed so we can run on SandyBridge.

v9:
* Fix wait on spinner.
* More tweaks to accuracy test.

v10:
* Dropped accuracy subtest changes due problems with RT thread and tasklet submission.

---
 tests/perf_pmu.c | 151 +++++++++++++++++++++++++++++++++++++------------------
 1 file changed, 103 insertions(+), 48 deletions(-)
tests/perf_pmu.c +103 -48
index 19fcc95ffc7f..203fe48955a8 100644
@@ -170,6 +170,56 @@ static unsigned int e2ring(int gem_fd, const struct intel_execution_engine2 *e)
 #define FLAG_LONG (16)
 #define FLAG_HANG (32)
 
+static igt_spin_t * __spin_poll(int fd, uint32_t ctx, unsigned long flags)
+{
+	if (gem_can_store_dword(fd, flags))
+		return __igt_spin_batch_new_poll(fd, ctx, flags);
+	else
+		return __igt_spin_batch_new(fd, ctx, flags, 0);
+}
+
+static unsigned long __spin_wait(int fd, igt_spin_t *spin)
+{
+	struct timespec start = { };
+
+	igt_nsec_elapsed(&start);
+
+	if (spin->running) {
+		unsigned long timeout = 0;
+
+		while (!*((volatile bool *)spin->running)) {
+			unsigned long t = igt_nsec_elapsed(&start);
+
+			if ((t - timeout) > 250e6) {
+				timeout = t;
+				igt_warn("Spinner not running after %.2fms\n",
+					 (double)t / 1e6);
+			}
+		}
+	} else {
+		igt_debug("__spin_wait - usleep mode\n");
+		usleep(500e3); /* Better than nothing! */
+	}
+
+	return igt_nsec_elapsed(&start);
+}
+
+static igt_spin_t * __spin_sync(int fd, uint32_t ctx, unsigned long flags)
+{
+	igt_spin_t *spin = __spin_poll(fd, ctx, flags);
+
+	__spin_wait(fd, spin);
+
+	return spin;
+}
+
+static igt_spin_t * spin_sync(int fd, uint32_t ctx, unsigned long flags)
+{
+	igt_require_gem(fd);
+
+	return __spin_sync(fd, ctx, flags);
+}
+
 static void end_spin(int fd, igt_spin_t *spin, unsigned int flags)
 {
 	if (!spin)
@@ -180,8 +230,25 @@ static void end_spin(int fd, igt_spin_t *spin, unsigned int flags)
 	if (flags & FLAG_SYNC)
 		gem_sync(fd, spin->handle);
 
-	if (flags & TEST_TRAILING_IDLE)
-		usleep(batch_duration_ns / 5000);
+	if (flags & TEST_TRAILING_IDLE) {
+		unsigned long t, timeout = 0;
+		struct timespec start = { };
+
+		igt_nsec_elapsed(&start);
+
+		do {
+			t = igt_nsec_elapsed(&start);
+
+			if (gem_bo_busy(fd, spin->handle) &&
+			    (t - timeout) > 10e6) {
+				timeout = t;
+				igt_warn("Spinner not idle after %.2fms\n",
+					 (double)t / 1e6);
+			}
+
+			usleep(1e3);
+		} while (t < batch_duration_ns / 5);
+	}
 }
 
 static void
@@ -195,7 +262,7 @@ single(int gem_fd, const struct intel_execution_engine2 *e, unsigned int flags)
 	fd = open_pmu(I915_PMU_ENGINE_BUSY(e->class, e->instance));
 
 	if (flags & TEST_BUSY)
-		spin = igt_spin_batch_new(gem_fd, 0, e2ring(gem_fd, e), 0);
+		spin = spin_sync(gem_fd, 0, e2ring(gem_fd, e));
 	else
 		spin = NULL;
 
@@ -251,13 +318,7 @@ busy_start(int gem_fd, const struct intel_execution_engine2 *e)
 	 */
 	sleep(2);
 
-	spin = __igt_spin_batch_new(gem_fd, 0, e2ring(gem_fd, e), 0);
-
-	/*
-	 * Sleep for a bit after making the engine busy to make sure the PMU
-	 * gets enabled when the batch is already running.
-	 */
-	usleep(500e3);
+	spin = __spin_sync(gem_fd, 0, e2ring(gem_fd, e));
 
 	fd = open_pmu(I915_PMU_ENGINE_BUSY(e->class, e->instance));
 
@@ -300,7 +361,7 @@ busy_double_start(int gem_fd, const struct intel_execution_engine2 *e)
 	 * re-submission in execlists mode. Make sure busyness is correctly
 	 * reported with the engine busy, and after the engine went idle.
 	 */
-	spin[0] = __igt_spin_batch_new(gem_fd, 0, e2ring(gem_fd, e), 0);
+	spin[0] = __spin_sync(gem_fd, 0, e2ring(gem_fd, e));
 	usleep(500e3);
 	spin[1] = __igt_spin_batch_new(gem_fd, ctx, e2ring(gem_fd, e), 0);
 
@@ -386,7 +447,7 @@ busy_check_all(int gem_fd, const struct intel_execution_engine2 *e,
 
 	igt_assert_eq(i, num_engines);
 
-	spin = igt_spin_batch_new(gem_fd, 0, e2ring(gem_fd, e), 0);
+	spin = spin_sync(gem_fd, 0, e2ring(gem_fd, e));
 	pmu_read_multi(fd[0], num_engines, tval[0]);
 	slept = measured_usleep(batch_duration_ns / 1000);
 	if (flags & TEST_TRAILING_IDLE)
@@ -412,15 +473,15 @@ busy_check_all(int gem_fd, const struct intel_execution_engine2 *e,
 }
 
 static void
-__submit_spin_batch(int gem_fd,
-		    struct drm_i915_gem_exec_object2 *obj,
-		    const struct intel_execution_engine2 *e)
+__submit_spin_batch(int gem_fd, igt_spin_t *spin,
+		    const struct intel_execution_engine2 *e,
+		    int offset)
 {
-	struct drm_i915_gem_execbuffer2 eb = {
-		.buffer_count = 1,
-		.buffers_ptr = to_user_pointer(obj),
-		.flags = e2ring(gem_fd, e),
-	};
+	struct drm_i915_gem_execbuffer2 eb = spin->execbuf;
+
+	eb.flags &= ~(0x3f | I915_EXEC_BSD_MASK);
+	eb.flags |= e2ring(gem_fd, e) | I915_EXEC_NO_RELOC;
+	eb.batch_start_offset += offset;
 
 	gem_execbuf(gem_fd, &eb);
 }
@@ -429,7 +490,6 @@ static void
 most_busy_check_all(int gem_fd, const struct intel_execution_engine2 *e,
 		    const unsigned int num_engines, unsigned int flags)
 {
-	struct drm_i915_gem_exec_object2 obj = {};
 	const struct intel_execution_engine2 *e_;
 	uint64_t tval[2][num_engines];
 	uint64_t val[num_engines];
@@ -443,15 +503,12 @@ most_busy_check_all(int gem_fd, const struct intel_execution_engine2 *e,
 		if (!gem_has_engine(gem_fd, e_->class, e_->instance))
 			continue;
 
-		if (e == e_) {
+		if (e == e_)
 			idle_idx = i;
-		} else if (spin) {
-			__submit_spin_batch(gem_fd, &obj, e_);
-		} else {
-			spin = igt_spin_batch_new(gem_fd, 0,
-						  e2ring(gem_fd, e_), 0);
-			obj.handle = spin->handle;
-		}
+		else if (spin)
+			__submit_spin_batch(gem_fd, spin, e_, 64);
+		else
+			spin = __spin_poll(gem_fd, 0, e2ring(gem_fd, e_));
 
 		val[i++] = I915_PMU_ENGINE_BUSY(e_->class, e_->instance);
 	}
@@ -461,6 +518,9 @@ most_busy_check_all(int gem_fd, const struct intel_execution_engine2 *e,
 	for (i = 0; i < num_engines; i++)
 		fd[i] = open_group(val[i], fd[0]);
 
+	/* Small delay to allow engines to start. */
+	usleep(__spin_wait(gem_fd, spin) * num_engines / 1e3);
+
 	pmu_read_multi(fd[0], num_engines, tval[0]);
 	slept = measured_usleep(batch_duration_ns / 1000);
 	if (flags & TEST_TRAILING_IDLE)
@@ -489,7 +549,6 @@ static void
 all_busy_check_all(int gem_fd, const unsigned int num_engines,
 		   unsigned int flags)
 {
-	struct drm_i915_gem_exec_object2 obj = {};
 	const struct intel_execution_engine2 *e;
 	uint64_t tval[2][num_engines];
 	uint64_t val[num_engines];
@@ -503,13 +562,10 @@ all_busy_check_all(int gem_fd, const unsigned int num_engines,
 		if (!gem_has_engine(gem_fd, e->class, e->instance))
 			continue;
 
-		if (spin) {
-			__submit_spin_batch(gem_fd, &obj, e);
-		} else {
-			spin = igt_spin_batch_new(gem_fd, 0,
-						  e2ring(gem_fd, e), 0);
-			obj.handle = spin->handle;
-		}
+		if (spin)
+			__submit_spin_batch(gem_fd, spin, e, 64);
+		else
+			spin = __spin_poll(gem_fd, 0, e2ring(gem_fd, e));
 
 		val[i++] = I915_PMU_ENGINE_BUSY(e->class, e->instance);
 	}
@@ -519,6 +575,9 @@ all_busy_check_all(int gem_fd, const unsigned int num_engines,
 	for (i = 0; i < num_engines; i++)
 		fd[i] = open_group(val[i], fd[0]);
 
+	/* Small delay to allow engines to start. */
+	usleep(__spin_wait(gem_fd, spin) * num_engines / 1e3);
+
 	pmu_read_multi(fd[0], num_engines, tval[0]);
 	slept = measured_usleep(batch_duration_ns / 1000);
 	if (flags & TEST_TRAILING_IDLE)
@@ -550,7 +609,7 @@ no_sema(int gem_fd, const struct intel_execution_engine2 *e, unsigned int flags)
 	open_group(I915_PMU_ENGINE_WAIT(e->class, e->instance), fd);
 
 	if (flags & TEST_BUSY)
-		spin = igt_spin_batch_new(gem_fd, 0, e2ring(gem_fd, e), 0);
+		spin = spin_sync(gem_fd, 0, e2ring(gem_fd, e));
 	else
 		spin = NULL;
 
@@ -884,7 +943,7 @@ multi_client(int gem_fd, const struct intel_execution_engine2 *e)
 	 */
 	fd[1] = open_pmu(config);
 
-	spin = igt_spin_batch_new(gem_fd, 0, e2ring(gem_fd, e), 0);
+	spin = spin_sync(gem_fd, 0, e2ring(gem_fd, e));
 
 	val[0] = val[1] = __pmu_read_single(fd[0], &ts[0]);
 	slept[1] = measured_usleep(batch_duration_ns / 1000);
@@ -1248,7 +1307,7 @@ test_frequency(int gem_fd)
 	igt_require(igt_sysfs_get_u32(sysfs, "gt_boost_freq_mhz") == min_freq);
 
 	gem_quiescent_gpu(gem_fd); /* Idle to be sure the change takes effect */
-	spin = igt_spin_batch_new(gem_fd, 0, I915_EXEC_RENDER, 0);
+	spin = spin_sync(gem_fd, 0, I915_EXEC_RENDER);
 
 	slept = pmu_read_multi(fd, 2, start);
 	measured_usleep(batch_duration_ns / 1000);
@@ -1274,7 +1333,7 @@ test_frequency(int gem_fd)
 	igt_require(igt_sysfs_get_u32(sysfs, "gt_min_freq_mhz") == max_freq);
 
 	gem_quiescent_gpu(gem_fd);
-	spin = igt_spin_batch_new(gem_fd, 0, I915_EXEC_RENDER, 0);
+	spin = spin_sync(gem_fd, 0, I915_EXEC_RENDER);
 
 	slept = pmu_read_multi(fd, 2, start);
 	measured_usleep(batch_duration_ns / 1000);
@@ -1517,7 +1576,6 @@ accuracy(int gem_fd, const struct intel_execution_engine2 *e,
 		const unsigned long timeout[] = {
 			pwm_calibration_us * 1000, test_us * 1000
 		};
-		struct drm_i915_gem_exec_object2 obj = {};
 		uint64_t total_busy_ns = 0, total_idle_ns = 0;
 		igt_spin_t *spin;
 		int ret;
@@ -1531,11 +1589,8 @@ accuracy(int gem_fd, const struct intel_execution_engine2 *e,
 
 		/* Allocate our spin batch and idle it. */
 		spin = igt_spin_batch_new(gem_fd, 0, e2ring(gem_fd, e), 0);
-		obj.handle = spin->handle;
-		__submit_spin_batch(gem_fd, &obj, e); /* record its location */
 		igt_spin_batch_end(spin);
-		gem_sync(gem_fd, obj.handle);
-		obj.flags |= EXEC_OBJECT_PINNED;
+		gem_sync(gem_fd, spin->handle);
 
 		/* 1st pass is calibration, second pass is the test. */
 		for (int pass = 0; pass < ARRAY_SIZE(timeout); pass++) {
@@ -1549,7 +1604,7 @@ accuracy(int gem_fd, const struct intel_execution_engine2 *e,
 
 				/* Restart the spinbatch. */
 				__rearm_spin_batch(spin);
-				__submit_spin_batch(gem_fd, &obj, e);
+				__submit_spin_batch(gem_fd, spin, e, 0);
 
 				/*
 				 * Note that the submission may be delayed to a
@@ -1559,7 +1614,7 @@ accuracy(int gem_fd, const struct intel_execution_engine2 *e,
 
 				t_busy = measured_usleep(busy_us);
 				igt_spin_batch_end(spin);
-				gem_sync(gem_fd, obj.handle);
+				gem_sync(gem_fd, spin->handle);
 
 				total_busy_ns += t_busy;
 
Signature · 6 lines
2.14.1

_______________________________________________
Intel-gfx mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
Reviewed-by on v1 1/3
#11 lore raw
In reply to Tvrtko Ursulin

Quoting Tvrtko Ursulin (2018-03-22 11:17:10)

Quoted 10 lines

From: Tvrtko Ursulin <[email protected]>

Pollable spin batch exports a spin->running pointer which can be checked by dereferencing it to see if the spinner is actually executing on the GPU.

This is useful for tests which want to make sure they do not proceed with their next step whilst the spinner is potentially only being processed by the driver and not actually executing.

Quoted 5 lines

Pollable spinner can be created with igt_spin_batch_new_poll or __igt_spin_batch_new_poll, after which a loop similar to:

        while (!*(volatile bool *)spin->running)
                ;

Let's actually make READ_ONCE() a common thing and make this a convenient igt_spin_busywait_until_running() (igt_spin_spin)?

Quoted 3 lines

Can be used to wait until the spinner start execution.

-Chris
_______________________________________________
Intel-gfx mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

#12 lore raw

[igt-dev] ✓ Fi.CI.BAT: success for series starting with [i-g-t,1/3] lib/dummyload: Add pollable spin batch (rev2)

In reply to Tvrtko Ursulin
== Series Details ==

== Series Details ==

Series: series starting with [i-g-t,1/3] lib/dummyload: Add pollable spin batch (rev2)
URL : https://patchwork.freedesktop.org/series/40453/
State : success

== Summary ==

IGT patchset tested on top of latest successful build c30e331188c97c9cb8e125abbfe56e1aea3241f7 lib/kms: Use named initializers for prop name arrays

with latest DRM-Tip kernel build CI_DRM_3966
dff9ece60048 drm-tip: 2018y-03m-21d-20h-44m-14s UTC integration manifest

No testlist changes.

---- Known issues:

Test debugfs_test:

        Subgroup read_all_entries:
                incomplete -> PASS       (fi-snb-2520m) fdo#103713
Test kms_flip:
        Subgroup basic-flip-vs-wf_vblank:
                fail       -> PASS       (fi-skl-6770hq) fdo#100368
Test kms_pipe_crc_basic:
        Subgroup suspend-read-crc-pipe-c:
                pass       -> INCOMPLETE (fi-hsw-4770) fdo#104944

fdo#103713 https://bugs.freedesktop.org/show_bug.cgi?id=103713
fdo#100368 https://bugs.freedesktop.org/show_bug.cgi?id=100368
fdo#104944 https://bugs.freedesktop.org/show_bug.cgi?id=104944

fi-bdw-5557u     total:285  pass:264  dwarn:0   dfail:0   fail:0   skip:21  time:430s
fi-bdw-gvtdvm    total:285  pass:261  dwarn:0   dfail:0   fail:0   skip:24  time:443s
fi-blb-e6850     total:285  pass:220  dwarn:1   dfail:0   fail:0   skip:64  time:385s
fi-bsw-n3050     total:285  pass:239  dwarn:0   dfail:0   fail:0   skip:46  time:547s
fi-bwr-2160      total:285  pass:180  dwarn:0   dfail:0   fail:0   skip:105 time:298s
fi-bxt-dsi       total:285  pass:255  dwarn:0   dfail:0   fail:0   skip:30  time:516s
fi-bxt-j4205     total:285  pass:256  dwarn:0   dfail:0   fail:0   skip:29  time:516s
fi-byt-j1900     total:285  pass:250  dwarn:0   dfail:0   fail:0   skip:35  time:517s
fi-byt-n2820     total:285  pass:246  dwarn:0   dfail:0   fail:0   skip:39  time:502s
fi-cfl-8700k     total:285  pass:257  dwarn:0   dfail:0   fail:0   skip:28  time:410s
fi-cnl-drrs      total:285  pass:254  dwarn:3   dfail:0   fail:0   skip:28  time:529s
fi-cnl-y3        total:285  pass:259  dwarn:0   dfail:0   fail:0   skip:26  time:588s
fi-elk-e7500     total:285  pass:225  dwarn:1   dfail:0   fail:0   skip:59  time:426s
fi-gdg-551       total:285  pass:176  dwarn:0   dfail:0   fail:1   skip:108 time:319s
fi-glk-1         total:285  pass:257  dwarn:0   dfail:0   fail:0   skip:28  time:538s
fi-hsw-4770      total:243  pass:219  dwarn:0   dfail:0   fail:0   skip:23 
fi-ilk-650       total:285  pass:225  dwarn:0   dfail:0   fail:0   skip:60  time:424s
fi-ivb-3520m     total:285  pass:256  dwarn:0   dfail:0   fail:0   skip:29  time:464s
fi-ivb-3770      total:285  pass:252  dwarn:0   dfail:0   fail:0   skip:33  time:432s
fi-kbl-7500u     total:285  pass:260  dwarn:1   dfail:0   fail:0   skip:24  time:474s
fi-kbl-7567u     total:285  pass:265  dwarn:0   dfail:0   fail:0   skip:20  time:467s
fi-kbl-r         total:285  pass:258  dwarn:0   dfail:0   fail:0   skip:27  time:516s
fi-pnv-d510      total:285  pass:219  dwarn:1   dfail:0   fail:0   skip:65  time:651s
fi-skl-6260u     total:285  pass:265  dwarn:0   dfail:0   fail:0   skip:20  time:436s
fi-skl-6600u     total:285  pass:258  dwarn:0   dfail:0   fail:0   skip:27  time:531s
fi-skl-6700k2    total:285  pass:261  dwarn:0   dfail:0   fail:0   skip:24  time:504s
fi-skl-6770hq    total:285  pass:265  dwarn:0   dfail:0   fail:0   skip:20  time:497s
fi-skl-guc       total:285  pass:257  dwarn:0   dfail:0   fail:0   skip:28  time:427s
fi-skl-gvtdvm    total:285  pass:262  dwarn:0   dfail:0   fail:0   skip:23  time:447s
fi-snb-2520m     total:285  pass:245  dwarn:0   dfail:0   fail:0   skip:40  time:578s
fi-snb-2600      total:285  pass:245  dwarn:0   dfail:0   fail:0   skip:40  time:405s

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_1179/issues.html
_______________________________________________
igt-dev mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/igt-dev

#13 lore raw

[igt-dev] ✗ Fi.CI.IGT: failure for series starting with [i-g-t,1/3] lib/dummyload: Add pollable spin batch (rev2)

In reply to Tvrtko Ursulin
== Series Details ==

== Series Details ==

Series: series starting with [i-g-t,1/3] lib/dummyload: Add pollable spin batch (rev2)
URL : https://patchwork.freedesktop.org/series/40453/
State : failure

== Summary ==

---- Possible new issues:

Test gem_eio:

        Subgroup wait:
                pass       -> FAIL       (shard-apl)
Test kms_cursor_legacy:
        Subgroup flip-vs-cursor-legacy:
                pass       -> FAIL       (shard-hsw)

---- Known issues:

Test kms_flip:

        Subgroup flip-vs-wf_vblank-interruptible:
                fail       -> PASS       (shard-hsw) fdo#100368
Test kms_frontbuffer_tracking:
        Subgroup fbc-1p-primscrn-pri-shrfb-draw-render:
                pass       -> FAIL       (shard-snb) fdo#103167
Test kms_plane_multiple:
        Subgroup atomic-pipe-a-tiling-x:
                fail       -> PASS       (shard-snb) fdo#103166
Test kms_setmode:
        Subgroup basic:
                pass       -> FAIL       (shard-apl) fdo#99912
Test kms_sysfs_edid_timing:
                pass       -> WARN       (shard-apl) fdo#100047

fdo#100368 https://bugs.freedesktop.org/show_bug.cgi?id=100368
fdo#103167 https://bugs.freedesktop.org/show_bug.cgi?id=103167
fdo#103166 https://bugs.freedesktop.org/show_bug.cgi?id=103166
fdo#99912 https://bugs.freedesktop.org/show_bug.cgi?id=99912
fdo#100047 https://bugs.freedesktop.org/show_bug.cgi?id=100047

shard-apl        total:3478 pass:1813 dwarn:1   dfail:0   fail:8   skip:1655 time:12969s
shard-hsw        total:3478 pass:1767 dwarn:1   dfail:0   fail:2   skip:1707 time:11706s
shard-snb        total:3478 pass:1357 dwarn:1   dfail:0   fail:3   skip:2117 time:7140s

Blacklisted hosts:
shard-kbl total:3478 pass:1940 dwarn:1 dfail:0 fail:9 skip:1528 time:9821s

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_1179/shards.html
_______________________________________________
igt-dev mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/igt-dev