[Intel-gfx] [PATCH igt 2/3] igt/gem_exec_schedule: Basic tests for preemption

Chris Wilson chris at chris-wilson.co.uk
Thu Aug 3 12:30:28 UTC 2017


We queue N low priority hanging batches across the engines
and check that out high priority write over takes them.

Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
---
 lib/igt_dummyload.c       |  20 ++++--
 lib/igt_dummyload.h       |  10 ++-
 tests/gem_busy.c          |   4 +-
 tests/gem_exec_fence.c    |   2 +-
 tests/gem_exec_reloc.c    |   6 +-
 tests/gem_exec_schedule.c | 156 ++++++++++++++++++++++++++++++++++++++++++++++
 tests/gem_shrink.c        |   4 +-
 tests/gem_spin_batch.c    |   4 +-
 tests/gem_wait.c          |   2 +-
 tests/kms_busy.c          |   8 ++-
 tests/kms_flip.c          |   4 +-
 tests/pm_rps.c            |   2 +-
 12 files changed, 197 insertions(+), 25 deletions(-)

diff --git a/lib/igt_dummyload.c b/lib/igt_dummyload.c
index 00c6a030..5ad386a5 100644
--- a/lib/igt_dummyload.c
+++ b/lib/igt_dummyload.c
@@ -46,6 +46,8 @@
 
 #define ENGINE_MASK  (I915_EXEC_RING_MASK | LOCAL_I915_EXEC_BSD_MASK)
 
+#define MI_ARB_CHK (0x5 << 23)
+
 static const int BATCH_SIZE = 4096;
 static IGT_LIST(spin_list);
 
@@ -61,7 +63,8 @@ fill_reloc(struct drm_i915_gem_relocation_entry *reloc,
 }
 
 static void emit_recursive_batch(igt_spin_t *spin,
-				 int fd, int engine, unsigned int dep)
+				 int fd, uint32_t ctx, unsigned engine,
+				 uint32_t dep)
 {
 #define SCRATCH 0
 #define BATCH 1
@@ -112,9 +115,13 @@ static void emit_recursive_batch(igt_spin_t *spin,
 	spin->batch = batch;
 	spin->handle = obj[BATCH].handle;
 
+	/* Allow ourselves to be preempted */
+	*batch++ = MI_ARB_CHK;
+
 	/* recurse */
 	fill_reloc(&relocs[obj[BATCH].relocation_count],
-		   obj[BATCH].handle, 1, I915_GEM_DOMAIN_COMMAND, 0);
+		   obj[BATCH].handle, (batch - spin->batch) + 1,
+		   I915_GEM_DOMAIN_COMMAND, 0);
 	if (gen >= 8) {
 		*batch++ = MI_BATCH_BUFFER_START | 1 << 8 | 1;
 		*batch++ = 0;
@@ -135,6 +142,7 @@ static void emit_recursive_batch(igt_spin_t *spin,
 	obj[BATCH].relocs_ptr = to_user_pointer(relocs);
 
 	execbuf.buffers_ptr = to_user_pointer(obj + (2 - execbuf.buffer_count));
+	execbuf.rsvd1 = ctx;
 
 	for (i = 0; i < nengine; i++) {
 		execbuf.flags &= ~ENGINE_MASK;
@@ -144,14 +152,14 @@ static void emit_recursive_batch(igt_spin_t *spin,
 }
 
 igt_spin_t *
-__igt_spin_batch_new(int fd, int engine, unsigned int dep)
+__igt_spin_batch_new(int fd, uint32_t ctx, unsigned engine, uint32_t dep)
 {
 	igt_spin_t *spin;
 
 	spin = calloc(1, sizeof(struct igt_spin));
 	igt_assert(spin);
 
-	emit_recursive_batch(spin, fd, engine, dep);
+	emit_recursive_batch(spin, fd, ctx, engine, dep);
 	igt_assert(gem_bo_busy(fd, spin->handle));
 
 	igt_list_add(&spin->link, &spin_list);
@@ -175,11 +183,11 @@ __igt_spin_batch_new(int fd, int engine, unsigned int dep)
  * Structure with helper internal state for igt_spin_batch_free().
  */
 igt_spin_t *
-igt_spin_batch_new(int fd, int engine, unsigned int dep)
+igt_spin_batch_new(int fd, uint32_t ctx, unsigned engine, uint32_t dep)
 {
 	igt_require_gem(fd);
 
-	return __igt_spin_batch_new(fd, engine, dep);
+	return __igt_spin_batch_new(fd, ctx, engine, dep);
 }
 
 static void notify(union sigval arg)
diff --git a/lib/igt_dummyload.h b/lib/igt_dummyload.h
index 340cada0..215425f7 100644
--- a/lib/igt_dummyload.h
+++ b/lib/igt_dummyload.h
@@ -37,8 +37,14 @@ typedef struct igt_spin {
 	uint32_t *batch;
 } igt_spin_t;
 
-igt_spin_t *__igt_spin_batch_new(int fd, int engine, unsigned int dep);
-igt_spin_t *igt_spin_batch_new(int fd, int engine, unsigned int dep);
+igt_spin_t *__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);
 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);
diff --git a/tests/gem_busy.c b/tests/gem_busy.c
index 8702dd7e..58aa5ee5 100644
--- a/tests/gem_busy.c
+++ b/tests/gem_busy.c
@@ -460,7 +460,7 @@ static bool has_semaphores(int fd)
 
 static bool has_extended_busy_ioctl(int fd)
 {
-	igt_spin_t *spin = igt_spin_batch_new(fd, I915_EXEC_RENDER, 0);
+	igt_spin_t *spin = igt_spin_batch_new(fd, 0, I915_EXEC_RENDER, 0);
 	uint32_t read, write;
 
 	__gem_busy(fd, spin->handle, &read, &write);
@@ -471,7 +471,7 @@ static bool has_extended_busy_ioctl(int fd)
 
 static void basic(int fd, unsigned ring, unsigned flags)
 {
-	igt_spin_t *spin = igt_spin_batch_new(fd, ring, 0);
+	igt_spin_t *spin = igt_spin_batch_new(fd, 0, ring, 0);
 	struct timespec tv;
 	int timeout;
 	bool busy;
diff --git a/tests/gem_exec_fence.c b/tests/gem_exec_fence.c
index b094f585..5361390e 100644
--- a/tests/gem_exec_fence.c
+++ b/tests/gem_exec_fence.c
@@ -424,7 +424,7 @@ static void test_parallel(int fd, unsigned int master)
 	/* Fill the queue with many requests so that the next one has to
 	 * wait before it can be executed by the hardware.
 	 */
-	spin = igt_spin_batch_new(fd, master, c.handle);
+	spin = igt_spin_batch_new(fd, 0, master, c.handle);
 	resubmit(fd, spin->handle, master, 16);
 
 	/* Now queue the master request and its secondaries */
diff --git a/tests/gem_exec_reloc.c b/tests/gem_exec_reloc.c
index edbc0f13..432a42a9 100644
--- a/tests/gem_exec_reloc.c
+++ b/tests/gem_exec_reloc.c
@@ -388,7 +388,7 @@ static void basic_reloc(int fd, unsigned before, unsigned after, unsigned flags)
 		}
 
 		if (flags & ACTIVE) {
-			spin = igt_spin_batch_new(fd, I915_EXEC_DEFAULT, obj.handle);
+			spin = igt_spin_batch_new(fd, 0, I915_EXEC_DEFAULT, obj.handle);
 			if (!(flags & HANG))
 				igt_spin_batch_set_timeout(spin, NSEC_PER_SEC/100);
 			igt_assert(gem_bo_busy(fd, obj.handle));
@@ -454,7 +454,7 @@ static void basic_reloc(int fd, unsigned before, unsigned after, unsigned flags)
 		}
 
 		if (flags & ACTIVE) {
-			spin = igt_spin_batch_new(fd, I915_EXEC_DEFAULT, obj.handle);
+			spin = igt_spin_batch_new(fd, 0, I915_EXEC_DEFAULT, obj.handle);
 			if (!(flags & HANG))
 				igt_spin_batch_set_timeout(spin, NSEC_PER_SEC/100);
 			igt_assert(gem_bo_busy(fd, obj.handle));
@@ -581,7 +581,7 @@ static void basic_range(int fd, unsigned flags)
 	execbuf.buffer_count = n + 1;
 
 	if (flags & ACTIVE) {
-		spin = igt_spin_batch_new(fd, 0, obj[n].handle);
+		spin = igt_spin_batch_new(fd, 0, 0, obj[n].handle);
 		if (!(flags & HANG))
 			igt_spin_batch_set_timeout(spin, NSEC_PER_SEC/100);
 		igt_assert(gem_bo_busy(fd, obj[n].handle));
diff --git a/tests/gem_exec_schedule.c b/tests/gem_exec_schedule.c
index 1f5ed82b..634f8d1a 100644
--- a/tests/gem_exec_schedule.c
+++ b/tests/gem_exec_schedule.c
@@ -373,6 +373,153 @@ static void promotion(int fd, unsigned ring)
 	munmap(ptr, 4096);
 }
 
+static void preempt(int fd, unsigned ring)
+{
+	uint32_t result = gem_create(fd, 4096);
+	uint32_t *ptr = gem_mmap__gtt(fd, result, 4096, PROT_READ);
+	igt_spin_t *spin[16];
+	uint32_t ctx[2];
+
+	ctx[LO] = gem_context_create(fd);
+	ctx_set_priority(fd, ctx[LO], -MAX_PRIO);
+
+	ctx[HI] = gem_context_create(fd);
+	ctx_set_priority(fd, ctx[HI], MAX_PRIO);
+
+	for (int n = 0; n < 16; n++) {
+		spin[n] = igt_spin_batch_new(fd, ctx[LO], ring, 0);
+		igt_debug("spin[%d].handle=%d\n", n, spin[n]->handle);
+
+		store_dword(fd, ctx[HI], ring, result, 0, n + 1, 0, I915_GEM_DOMAIN_RENDER);
+
+		gem_set_domain(fd, result, I915_GEM_DOMAIN_GTT, 0);
+		igt_assert_eq_u32(ptr[0], n + 1);
+		igt_assert(gem_bo_busy(fd, spin[0]->handle));
+	}
+
+	for (int n = 0; n < 16; n++)
+		igt_spin_batch_free(fd, spin[n]);
+
+	gem_context_destroy(fd, ctx[LO]);
+	gem_context_destroy(fd, ctx[HI]);
+
+	munmap(ptr, 4096);
+	gem_close(fd, result);
+}
+
+static void preempt_other(int fd, unsigned ring)
+{
+	uint32_t result = gem_create(fd, 4096);
+	uint32_t *ptr = gem_mmap__gtt(fd, result, 4096, PROT_READ);
+	igt_spin_t *spin[16];
+	unsigned int other;
+	unsigned int n, i;
+	uint32_t ctx[3];
+
+	/* On each engine, insert
+	 * [NOISE] spinner,
+	 * [LOW] write
+	 *
+	 * Then on our target engine do a [HIGH] write which should then
+	 * prompt its dependent LOW writes in front of the spinner on
+	 * each engine. The purpose of this test is to check that preemption
+	 * can cross engines.
+	 */
+
+	ctx[LO] = gem_context_create(fd);
+	ctx_set_priority(fd, ctx[LO], -MAX_PRIO);
+
+	ctx[NOISE] = gem_context_create(fd);
+
+	ctx[HI] = gem_context_create(fd);
+	ctx_set_priority(fd, ctx[HI], MAX_PRIO);
+
+	n = 0;
+	for_each_engine(fd, other) {
+		spin[n] = igt_spin_batch_new(fd, ctx[NOISE], other, 0);
+		store_dword(fd, ctx[LO], other,
+			    result, (n + 1)*sizeof(uint32_t), n + 1,
+			    0, I915_GEM_DOMAIN_RENDER);
+		n++;
+	}
+	store_dword(fd, ctx[HI], ring,
+		    result, (n + 1)*sizeof(uint32_t), n + 1,
+		    0, I915_GEM_DOMAIN_RENDER);
+
+	gem_set_domain(fd, result, I915_GEM_DOMAIN_GTT, 0);
+
+	for (i = 0; i < n; i++) {
+		igt_assert(gem_bo_busy(fd, spin[i]->handle));
+		igt_spin_batch_free(fd, spin[i]);
+	}
+
+	n++;
+	for (i = 0; i <= n; i++)
+		igt_assert_eq_u32(ptr[i], i);
+
+	gem_context_destroy(fd, ctx[LO]);
+	gem_context_destroy(fd, ctx[NOISE]);
+	gem_context_destroy(fd, ctx[HI]);
+
+	munmap(ptr, 4096);
+	gem_close(fd, result);
+}
+
+static void preempt_self(int fd, unsigned ring)
+{
+	uint32_t result = gem_create(fd, 4096);
+	uint32_t *ptr = gem_mmap__gtt(fd, result, 4096, PROT_READ);
+	igt_spin_t *spin[16];
+	unsigned int other;
+	unsigned int n, i;
+	uint32_t ctx[3];
+
+	/* On each engine, insert
+	 * [NOISE] spinner,
+	 * [self/LOW] write
+	 *
+	 * Then on our target engine do a [self/HIGH] write which should then
+	 * prompt its dependent self/LOW writes in front of the spinner on
+	 * each engine. The purpose of this test is to check that preemption
+	 * can cross engines.
+	 */
+
+	ctx[NOISE] = gem_context_create(fd);
+
+	ctx[HI] = gem_context_create(fd);
+
+	n = 0;
+	ctx_set_priority(fd, ctx[HI], -MAX_PRIO);
+	for_each_engine(fd, other) {
+		spin[n] = igt_spin_batch_new(fd, ctx[NOISE], other, 0);
+		store_dword(fd, ctx[HI], other,
+			    result, (n + 1)*sizeof(uint32_t), n + 1,
+			    0, I915_GEM_DOMAIN_RENDER);
+		n++;
+	}
+	ctx_set_priority(fd, ctx[HI], MAX_PRIO);
+	store_dword(fd, ctx[HI], ring,
+		    result, (n + 1)*sizeof(uint32_t), n + 1,
+		    0, I915_GEM_DOMAIN_RENDER);
+
+	gem_set_domain(fd, result, I915_GEM_DOMAIN_GTT, 0);
+
+	for (i = 0; i < n; i++) {
+		igt_assert(gem_bo_busy(fd, spin[i]->handle));
+		igt_spin_batch_free(fd, spin[i]);
+	}
+
+	n++;
+	for (i = 0; i <= n; i++)
+		igt_assert_eq_u32(ptr[i], i);
+
+	gem_context_destroy(fd, ctx[NOISE]);
+	gem_context_destroy(fd, ctx[HI]);
+
+	munmap(ptr, 4096);
+	gem_close(fd, result);
+}
+
 static void deep(int fd, unsigned ring)
 {
 #define XS 8
@@ -726,6 +873,15 @@ igt_main
 				igt_subtest_f("promotion-%s", e->name)
 					promotion(fd, e->exec_id | e->flags);
 
+				igt_subtest_f("preempt-%s", e->name)
+					preempt(fd, e->exec_id | e->flags);
+
+				igt_subtest_f("preempt-other-%s", e->name)
+					preempt_other(fd, e->exec_id | e->flags);
+
+				igt_subtest_f("preempt-self-%s", e->name)
+					preempt_self(fd, e->exec_id | e->flags);
+
 				igt_subtest_f("deep-%s", e->name)
 					deep(fd, e->exec_id | e->flags);
 
diff --git a/tests/gem_shrink.c b/tests/gem_shrink.c
index 8b09fc80..06f7a301 100644
--- a/tests/gem_shrink.c
+++ b/tests/gem_shrink.c
@@ -311,9 +311,9 @@ static void reclaim(unsigned engine, int timeout)
 		} while (!*shared);
 	}
 
-	spin = igt_spin_batch_new(fd, engine, 0);
+	spin = igt_spin_batch_new(fd, 0, engine, 0);
 	igt_until_timeout(timeout) {
-		igt_spin_t *next = __igt_spin_batch_new(fd, engine, 0);
+		igt_spin_t *next = __igt_spin_batch_new(fd, 0, engine, 0);
 
 		igt_spin_batch_set_timeout(spin, timeout_100ms);
 		gem_sync(fd, spin->handle);
diff --git a/tests/gem_spin_batch.c b/tests/gem_spin_batch.c
index 941aa139..24c5d9b7 100644
--- a/tests/gem_spin_batch.c
+++ b/tests/gem_spin_batch.c
@@ -41,9 +41,9 @@ static void spin(int fd, unsigned int engine, unsigned int timeout_sec)
 	struct timespec itv = { };
 	uint64_t elapsed;
 
-	spin = igt_spin_batch_new(fd, engine, 0);
+	spin = igt_spin_batch_new(fd, 0, engine, 0);
 	while ((elapsed = igt_nsec_elapsed(&tv)) >> 30 < timeout_sec) {
-		igt_spin_t *next = __igt_spin_batch_new(fd, engine, 0);
+		igt_spin_t *next = __igt_spin_batch_new(fd, 0, engine, 0);
 
 		igt_spin_batch_set_timeout(spin,
 					   timeout_100ms - igt_nsec_elapsed(&itv));
diff --git a/tests/gem_wait.c b/tests/gem_wait.c
index 591690ad..cf8c8154 100644
--- a/tests/gem_wait.c
+++ b/tests/gem_wait.c
@@ -110,7 +110,7 @@ static void unplug(struct cork *c)
 static void basic(int fd, unsigned engine, unsigned flags)
 {
 	struct cork cork = plug(fd, flags);
-	igt_spin_t *spin = igt_spin_batch_new(fd, engine, cork.handle);
+	igt_spin_t *spin = igt_spin_batch_new(fd, 0, engine, cork.handle);
 	struct drm_i915_gem_wait wait = {
 	       	flags & WRITE ? cork.handle : spin->handle
        	};
diff --git a/tests/kms_busy.c b/tests/kms_busy.c
index af7335db..7ff461a9 100644
--- a/tests/kms_busy.c
+++ b/tests/kms_busy.c
@@ -91,7 +91,8 @@ static void flip_to_fb(igt_display_t *dpy, int pipe,
 	struct timespec tv = { 1, 0 };
 	struct drm_event_vblank ev;
 
-	igt_spin_t *t = igt_spin_batch_new(dpy->drm_fd, ring, fb->gem_handle);
+	igt_spin_t *t = igt_spin_batch_new(dpy->drm_fd,
+					   0, ring, fb->gem_handle);
 
 	if (modeset) {
 		/*
@@ -203,7 +204,8 @@ static void test_flip(igt_display_t *dpy, unsigned ring, int pipe, bool modeset)
 static void test_atomic_commit_hang(igt_display_t *dpy, igt_plane_t *primary,
 				    struct igt_fb *busy_fb, unsigned ring)
 {
-	igt_spin_t *t = igt_spin_batch_new(dpy->drm_fd, ring, busy_fb->gem_handle);
+	igt_spin_t *t = igt_spin_batch_new(dpy->drm_fd,
+					   0, ring, busy_fb->gem_handle);
 	struct pollfd pfd = { .fd = dpy->drm_fd, .events = POLLIN };
 	unsigned flags = 0;
 	struct drm_event_vblank ev;
@@ -290,7 +292,7 @@ static void test_pageflip_modeset_hang(igt_display_t *dpy,
 
 	igt_display_commit2(dpy, dpy->is_atomic ? COMMIT_ATOMIC : COMMIT_LEGACY);
 
-	t = igt_spin_batch_new(dpy->drm_fd, ring, fb.gem_handle);
+	t = igt_spin_batch_new(dpy->drm_fd, 0, ring, fb.gem_handle);
 
 	do_or_die(drmModePageFlip(dpy->drm_fd, dpy->pipes[pipe].crtc_id, fb.fb_id, DRM_MODE_PAGE_FLIP_EVENT, &fb));
 
diff --git a/tests/kms_flip.c b/tests/kms_flip.c
index ede5fd2b..7fd18587 100644
--- a/tests/kms_flip.c
+++ b/tests/kms_flip.c
@@ -694,11 +694,11 @@ static unsigned int run_test_step(struct test_output *o)
 		o->current_fb_id = !o->current_fb_id;
 
 	if (o->flags & TEST_WITH_DUMMY_BCS)
-		spin_bcs = igt_spin_batch_new(drm_fd, I915_EXEC_BLT,
+		spin_bcs = igt_spin_batch_new(drm_fd, 0, I915_EXEC_BLT,
 					      o->fb_info[o->current_fb_id].gem_handle);
 
 	if (o->flags & TEST_WITH_DUMMY_RCS)
-		spin_rcs = igt_spin_batch_new(drm_fd, I915_EXEC_RENDER,
+		spin_rcs = igt_spin_batch_new(drm_fd, 0, I915_EXEC_RENDER,
 					      o->fb_info[o->current_fb_id].gem_handle);
 
 	if (o->flags & TEST_FB_RECREATE)
diff --git a/tests/pm_rps.c b/tests/pm_rps.c
index f0455e78..ce01c466 100644
--- a/tests/pm_rps.c
+++ b/tests/pm_rps.c
@@ -566,7 +566,7 @@ static void boost_freq(int fd, int *boost_freqs)
 	int ring = -1;
 	igt_spin_t *load;
 
-	load = igt_spin_batch_new(fd, ring, 0);
+	load = igt_spin_batch_new(fd, 0, ring, 0);
 
 	/* Waiting will grant us a boost to maximum */
 	gem_wait(fd, load->handle, &timeout);
-- 
2.13.3



More information about the Intel-gfx mailing list