diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/gem_exec_fence.c | 214 |
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(); } |