Check that we can reorder batches around userspace sempahore waits by
injecting a semaphore that is only released by a later context.

Signed-off-by: Chris Wilson <ch...@chris-wilson.co.uk>
---
 tests/i915/gem_exec_schedule.c | 143 +++++++++++++++++++++++++++++++++
 1 file changed, 143 insertions(+)

diff --git a/tests/i915/gem_exec_schedule.c b/tests/i915/gem_exec_schedule.c
index 9a0795281..812197770 100644
--- a/tests/i915/gem_exec_schedule.c
+++ b/tests/i915/gem_exec_schedule.c
@@ -52,6 +52,15 @@
 #define LOCAL_I915_EXEC_BSD_MASK       (3 << LOCAL_I915_EXEC_BSD_SHIFT)
 #define ENGINE_MASK  (I915_EXEC_RING_MASK | LOCAL_I915_EXEC_BSD_MASK)
 
+#define MI_SEMAPHORE_WAIT              (0x1c << 23)
+#define   MI_SEMAPHORE_POLL             (1 << 15)
+#define   MI_SEMAPHORE_SAD_GT_SDD       (0 << 12)
+#define   MI_SEMAPHORE_SAD_GTE_SDD      (1 << 12)
+#define   MI_SEMAPHORE_SAD_LT_SDD       (2 << 12)
+#define   MI_SEMAPHORE_SAD_LTE_SDD      (3 << 12)
+#define   MI_SEMAPHORE_SAD_EQ_SDD       (4 << 12)
+#define   MI_SEMAPHORE_SAD_NEQ_SDD      (5 << 12)
+
 IGT_TEST_DESCRIPTION("Check that we can control the order of execution");
 
 static inline
@@ -463,6 +472,138 @@ static void semaphore_codependency(int i915)
        }
 }
 
+static unsigned int offset_in_page(void *addr)
+{
+       return (uintptr_t)addr & 4095;
+}
+
+static void semaphore_resolve(int i915)
+{
+       const uint32_t SEMAPHORE_ADDR = 64 << 10;
+       uint32_t semaphore, outer, inner, *sema;
+       unsigned int engine;
+
+       /*
+        * Userspace may submit batches that wait upon unresolved
+        * semaphores. Ideally, we want to put those blocking batches
+        * to the back of the execution queue if we have something else
+        * that is ready to run right away. This test exploits a failure
+        * to reorder batches around a blocking semaphore by submitting
+        * the release of that semaphore from a later context.
+        */
+
+       igt_require(gem_scheduler_has_preemption(i915));
+       igt_assert(intel_get_drm_devid(i915) >= 8);
+
+       outer = gem_context_create(i915);
+       inner = gem_context_create(i915);
+
+       semaphore = gem_create(i915, 4096);
+       sema = gem_mmap__wc(i915, semaphore, 0, 4096, PROT_WRITE);
+
+       for_each_physical_engine(i915, engine) {
+               struct drm_i915_gem_exec_object2 obj[3];
+               struct drm_i915_gem_execbuffer2 eb;
+               uint32_t handle, cancel;
+               uint32_t *cs, *map;
+               igt_spin_t *spin;
+
+               if (!gem_can_store_dword(i915, engine))
+                       continue;
+
+               spin = __igt_spin_new(i915, .engine = engine);
+               cancel = *spin->batch;
+               igt_spin_end(spin); /* we just want its address for later */
+               gem_sync(i915, spin->handle);
+               *spin->batch = cancel;
+
+               handle = gem_create(i915, 4096);
+               cs = map = gem_mmap__cpu(i915, handle, 0, 4096, PROT_WRITE);
+
+               /* Set semaphore initially to 1 for polling and signaling */
+               *cs++ = MI_STORE_DWORD_IMM;
+               *cs++ = SEMAPHORE_ADDR;
+               *cs++ = 0;
+               *cs++ = 1;
+
+               /* Wait until another batch writes to our semaphore */
+               *cs++ = MI_SEMAPHORE_WAIT |
+                       MI_SEMAPHORE_POLL |
+                       MI_SEMAPHORE_SAD_EQ_SDD |
+                       (4 - 2);
+               *cs++ = 0;
+               *cs++ = SEMAPHORE_ADDR;
+               *cs++ = 0;
+
+               /* Then cancel the spinner */
+               *cs++ = MI_STORE_DWORD_IMM;
+               *cs++ = spin->obj[1].offset + offset_in_page(spin->batch);
+               *cs++ = 0;
+               *cs++ = MI_BATCH_BUFFER_END;
+
+               *cs++ = MI_BATCH_BUFFER_END;
+               munmap(map, 4096);
+
+               memset(&eb, 0, sizeof(eb));
+
+               /* First up is our spinning semaphore */
+               memset(obj, 0, sizeof(obj));
+               obj[0] = spin->obj[1];
+               obj[1].handle = semaphore;
+               obj[1].offset = SEMAPHORE_ADDR;
+               obj[1].flags = EXEC_OBJECT_PINNED;
+               obj[2].handle = handle;
+               eb.buffer_count = 3;
+               eb.buffers_ptr = to_user_pointer(obj);
+               eb.rsvd1 = outer;
+               gem_execbuf(i915, &eb);
+
+               /* Then add the GPU hang intermediatory */
+               memset(obj, 0, sizeof(obj));
+               obj[0].handle = handle;
+               obj[0].flags = EXEC_OBJECT_WRITE; /* always after semaphore */
+               obj[1] = spin->obj[1];
+               eb.buffer_count = 2;
+               eb.rsvd1 = 0;
+               gem_execbuf(i915, &eb);
+
+               while (READ_ONCE(*sema) == 0)
+                       ;
+
+               /* Now the semaphore is spinning, cancel it */
+               cancel = gem_create(i915, 4096);
+               cs = map = gem_mmap__cpu(i915, cancel, 0, 4096, PROT_WRITE);
+               *cs++ = MI_STORE_DWORD_IMM;
+               *cs++ = SEMAPHORE_ADDR;
+               *cs++ = 0;
+               *cs++ = 0;
+               *cs++ = MI_BATCH_BUFFER_END;
+               munmap(map, 4096);
+
+               memset(obj, 0, sizeof(obj));
+               obj[0].handle = semaphore;
+               obj[0].offset = SEMAPHORE_ADDR;
+               obj[0].flags = EXEC_OBJECT_PINNED;
+               obj[1].handle = cancel;
+               eb.buffer_count = 2;
+               eb.rsvd1 = inner;
+               gem_execbuf(i915, &eb);
+               gem_close(i915, cancel);
+
+               gem_sync(i915, handle); /* To hang unless cancel runs! */
+               gem_close(i915, handle);
+               igt_spin_free(i915, spin);
+
+               igt_assert_eq(*sema, 0);
+       }
+
+       munmap(sema, 4096);
+       gem_close(i915, semaphore);
+
+       gem_context_destroy(i915, inner);
+       gem_context_destroy(i915, outer);
+}
+
 static void reorder(int fd, unsigned ring, unsigned flags)
 #define EQUAL 1
 {
@@ -1450,6 +1591,8 @@ igt_main
                        semaphore_userlock(fd);
                igt_subtest("semaphore-codependency")
                        semaphore_codependency(fd);
+               igt_subtest("semaphore-resolve")
+                       semaphore_resolve(fd);
 
                igt_subtest("smoketest-all")
                        smoketest(fd, ALL_ENGINES, 30);
-- 
2.20.1

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

Reply via email to