summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorChris Wilson <chris@chris-wilson.co.uk>2017-06-30 15:00:52 +0100
committerChris Wilson <chris@chris-wilson.co.uk>2017-06-30 16:47:35 +0100
commit7c4e91a41fbe01eb3595a9dcaa0bb221d9b6b058 (patch)
tree3fa9f5f37ea7e4ee1a73521f842ef08c76ba3d69 /tests
parentbe2e7badd750858a499c862620768cffcac20295 (diff)
igt/gem_exec_fence: Test EXEC_FENCE_SUBMIT
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Diffstat (limited to 'tests')
-rw-r--r--tests/gem_exec_fence.c214
1 files changed, 211 insertions, 3 deletions
diff --git a/tests/gem_exec_fence.c b/tests/gem_exec_fence.c
index 5230e693..b094f585 100644
--- a/tests/gem_exec_fence.c
+++ b/tests/gem_exec_fence.c
@@ -34,6 +34,7 @@ IGT_TEST_DESCRIPTION("Check that execbuf waits for explicit fences");
#define LOCAL_EXEC_FENCE_IN (1 << 16)
#define LOCAL_EXEC_FENCE_OUT (1 << 17)
+#define LOCAL_EXEC_FENCE_SUBMIT (1 << 19)
#ifndef SYNC_IOC_MERGE
struct sync_merge_data {
@@ -300,6 +301,18 @@ static void test_fence_await(int fd, unsigned ring, unsigned flags)
gem_close(fd, scratch);
}
+static void resubmit(int fd, uint32_t handle, unsigned int ring, int count)
+{
+ struct drm_i915_gem_exec_object2 obj = { .handle = handle };
+ struct drm_i915_gem_execbuffer2 execbuf = {
+ .buffers_ptr = to_user_pointer(&obj),
+ .buffer_count = 1,
+ .flags = ring,
+ };
+ while (count--)
+ gem_execbuf(fd, &execbuf);
+}
+
struct cork {
int device;
uint32_t handle;
@@ -323,9 +336,10 @@ static void plug(int fd, struct cork *c)
close(dmabuf);
}
-static void unplug(struct cork *c)
+static void unplug(int fd, struct cork *c)
{
vgem_fence_signal(c->device, c->fence);
+ gem_close(fd, c->handle);
close(c->device);
}
@@ -382,12 +396,182 @@ static unsigned int measure_ring_size(int fd)
memset(&itv, 0, sizeof(itv));
setitimer(ITIMER_REAL, &itv, NULL);
- unplug(&c);
+ unplug(fd, &c);
gem_close(fd, obj[1].handle);
return count;
}
+static void test_parallel(int fd, unsigned int master)
+{
+ const int SCRATCH = 0;
+ const int 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 reloc[2];
+ struct drm_i915_gem_execbuffer2 execbuf;
+ uint32_t scratch = gem_create(fd, 4096);
+ uint32_t *out = gem_mmap__wc(fd, scratch, 0, 4096, PROT_READ);
+ uint32_t handle[16];
+ uint32_t batch[16];
+ igt_spin_t *spin;
+ unsigned engine;
+ struct cork c;
+ int i, x = 0;
+
+ plug(fd, &c);
+
+ /* 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);
+ resubmit(fd, spin->handle, master, 16);
+
+ /* Now queue the master request and its secondaries */
+ memset(&execbuf, 0, sizeof(execbuf));
+ execbuf.buffers_ptr = to_user_pointer(obj);
+ execbuf.buffer_count = 2;
+ execbuf.flags = master | LOCAL_EXEC_FENCE_OUT;
+ if (gen < 6)
+ execbuf.flags |= I915_EXEC_SECURE;
+
+ memset(obj, 0, sizeof(obj));
+ obj[SCRATCH].handle = scratch;
+
+ obj[BATCH].handle = gem_create(fd, 4096);
+ handle[x] = obj[BATCH].handle;
+ obj[BATCH].relocs_ptr = to_user_pointer(&reloc);
+ obj[BATCH].relocation_count = 2;
+ memset(reloc, 0, sizeof(reloc));
+
+ i = 0;
+
+ reloc[0].target_handle = obj[SCRATCH].handle;
+ reloc[0].presumed_offset = -1;
+ reloc[0].offset = sizeof(uint32_t) * (i + 1);
+ reloc[0].delta = sizeof(uint32_t) * x++;
+ reloc[0].read_domains = I915_GEM_DOMAIN_INSTRUCTION;
+ reloc[0].write_domain = 0; /* lies */
+
+ batch[i] = MI_STORE_DWORD_IMM | (gen < 6 ? 1 << 22 : 0);
+ if (gen >= 8) {
+ batch[++i] = reloc[0].presumed_offset + reloc[0].delta;
+ batch[++i] = (reloc[0].presumed_offset + reloc[0].delta) >> 32;
+ } else if (gen >= 4) {
+ batch[++i] = 0;
+ batch[++i] = reloc[0].presumed_offset + reloc[0].delta;
+ reloc[0].offset += sizeof(uint32_t);
+ } else {
+ batch[i]--;
+ batch[++i] = reloc[0].presumed_offset + reloc[0].delta;
+ }
+ batch[++i] = ~0u ^ x;
+
+ reloc[1].target_handle = obj[BATCH].handle; /* recurse */
+ reloc[1].presumed_offset = 0;
+ reloc[1].offset = sizeof(uint32_t) * (i + 2);
+ reloc[1].delta = 0;
+ reloc[1].read_domains = I915_GEM_DOMAIN_COMMAND;
+ reloc[1].write_domain = 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[1].delta = 1;
+ }
+ }
+ batch[++i] = MI_BATCH_BUFFER_END;
+ igt_assert(i < sizeof(batch)/sizeof(batch[0]));
+ gem_write(fd, obj[BATCH].handle, 0, batch, sizeof(batch));
+ gem_execbuf_wr(fd, &execbuf);
+
+ igt_assert(execbuf.rsvd2);
+ execbuf.rsvd2 >>= 32; /* out fence -> in fence */
+ obj[BATCH].relocation_count = 1;
+
+ /* Queue all secondaries */
+ for_each_engine(fd, engine) {
+ if (engine == 0 || engine == I915_EXEC_BSD)
+ continue;
+
+ if (engine == master)
+ continue;
+
+ execbuf.flags = engine | LOCAL_EXEC_FENCE_SUBMIT;
+ if (gen < 6)
+ execbuf.flags |= I915_EXEC_SECURE;
+
+ obj[BATCH].handle = gem_create(fd, 4096);
+ handle[x] = obj[BATCH].handle;
+
+ i = 0;
+ reloc[0].delta = sizeof(uint32_t) * x++;
+ batch[i] = MI_STORE_DWORD_IMM | (gen < 6 ? 1 << 22 : 0);
+ if (gen >= 8) {
+ batch[++i] = reloc[0].presumed_offset + reloc[0].delta;
+ batch[++i] = (reloc[0].presumed_offset + reloc[0].delta) >> 32;
+ } else if (gen >= 4) {
+ batch[++i] = 0;
+ batch[++i] = reloc[0].presumed_offset + reloc[0].delta;
+ } else {
+ batch[i]--;
+ batch[++i] = reloc[0].presumed_offset + reloc[0].delta;
+ }
+ batch[++i] = ~0u ^ x;
+ batch[++i] = MI_BATCH_BUFFER_END;
+ gem_write(fd, obj[BATCH].handle, 0, batch, sizeof(batch));
+ gem_execbuf(fd, &execbuf);
+ }
+ igt_assert(gem_bo_busy(fd, spin->handle));
+ close(execbuf.rsvd2);
+
+ /* No secondary should be executed since master is stalled. If there
+ * was no dependency chain at all, the secondaries would start
+ * immediately.
+ */
+ for (i = 0; i < x; i++) {
+ igt_assert_eq_u32(out[i], 0);
+ igt_assert(gem_bo_busy(fd, handle[i]));
+ }
+
+ /* Unblock the master */
+ unplug(fd, &c);
+ igt_spin_batch_end(spin);
+
+ /* Wait for all secondaries to complete. If we used a regular fence
+ * then the secondaries would not start until the master was complete.
+ * In this case that can only happen with a GPU reset, and so we run
+ * under the hang detector and double check that the master is still
+ * running afterwards.
+ */
+ for (i = 1; i < x; i++) {
+ while (gem_bo_busy(fd, handle[i]))
+ sleep(0);
+
+ igt_assert_f(out[i], "Missing output from engine %d\n", i);
+ gem_close(fd, handle[i]);
+ }
+ munmap(out, 4096);
+ gem_close(fd, obj[SCRATCH].handle);
+
+ /* Master should still be spinning, but all output should be written */
+ igt_assert(gem_bo_busy(fd, handle[0]));
+ out = gem_mmap__wc(fd, handle[0], 0, 4096, PROT_WRITE);
+ out[0] = MI_BATCH_BUFFER_END;
+ munmap(out, 4096);
+ gem_close(fd, handle[0]);
+}
+
#define EXPIRED 0x10000
static void test_long_history(int fd, long ring_size, unsigned flags)
{
@@ -463,7 +647,7 @@ static void test_long_history(int fd, long ring_size, unsigned flags)
if (!--limit)
break;
}
- unplug(&c);
+ unplug(fd, &c);
igt_info("History depth = %d\n", sync_fence_count(all_fences));
@@ -528,6 +712,21 @@ out:
return result;
}
+static bool has_submit_fence(int fd)
+{
+ struct drm_i915_getparam gp;
+ int value = 0;
+
+ memset(&gp, 0, sizeof(gp));
+ gp.param = 49; /* I915_PARAM_HAS_EXEC_SUBMIT_FENCE */
+ gp.value = &value;
+
+ ioctl(fd, DRM_IOCTL_I915_GETPARAM, &gp, sizeof(gp));
+ errno = 0;
+
+ return value;
+}
+
igt_main
{
const struct intel_execution_engine *e;
@@ -572,6 +771,15 @@ igt_main
igt_subtest_f("nb-await-%s", e->name)
test_fence_await(i915, e->exec_id | e->flags, NONBLOCK);
+ if (e->exec_id &&
+ !(e->exec_id == I915_EXEC_BSD && !e->flags)) {
+ igt_subtest_f("parallel-%s", e->name) {
+ igt_require(has_submit_fence(i915));
+ igt_until_timeout(2)
+ test_parallel(i915, e->exec_id | e->flags);
+ }
+ }
+
igt_fixture {
igt_stop_hang_detector();
}