From: Chris Wilson <chris@chris-wilson.co.uk> To: intel-gfx@lists.freedesktop.org Cc: igt-dev@lists.freedesktop.org, Chris Wilson <chris@chris-wilson.co.uk> Subject: [Intel-gfx] [PATCH i-g-t 2/2] i915/gem_exec_fence: Teach invalid-wait about invalid future fences Date: Wed, 13 May 2020 18:02:24 +0100 [thread overview] Message-ID: <20200513170224.848726-2-chris@chris-wilson.co.uk> (raw) In-Reply-To: <20200513170224.848726-1-chris@chris-wilson.co.uk> When we allow a wait on a future future fence, it must autoexpire if the fence is never signaled by userspace. Also put future fences to work, as the intention is to use them, along with WAIT_SUBMIT and semaphores, for userspace to perform its own fine-grained scheduling. Or simply run concurrent clients without having to flush batches between context switches. v2: Verify deadlock detection Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> --- tests/i915/gem_exec_fence.c | 680 +++++++++++++++++++++++++++++++++++- 1 file changed, 677 insertions(+), 3 deletions(-) diff --git a/tests/i915/gem_exec_fence.c b/tests/i915/gem_exec_fence.c index 4140bff24..eb1165080 100644 --- a/tests/i915/gem_exec_fence.c +++ b/tests/i915/gem_exec_fence.c @@ -1123,11 +1123,12 @@ static void test_syncobj_invalid_wait(int fd) struct drm_i915_gem_exec_fence fence = { .handle = syncobj_create(fd, 0), }; + int out; memset(&execbuf, 0, sizeof(execbuf)); execbuf.buffers_ptr = to_user_pointer(&obj); execbuf.buffer_count = 1; - execbuf.flags = I915_EXEC_FENCE_ARRAY; + execbuf.flags = I915_EXEC_FENCE_ARRAY | I915_EXEC_FENCE_OUT; execbuf.cliprects_ptr = to_user_pointer(&fence); execbuf.num_cliprects = 1; @@ -1135,14 +1136,59 @@ static void test_syncobj_invalid_wait(int fd) obj.handle = gem_create(fd, 4096); gem_write(fd, obj.handle, 0, &bbe, sizeof(bbe)); - /* waiting before the fence is set is invalid */ + /* waiting before the fence is set is^W may be invalid */ fence.flags = I915_EXEC_FENCE_WAIT; - igt_assert_eq(__gem_execbuf(fd, &execbuf), -EINVAL); + if (__gem_execbuf_wr(fd, &execbuf)) { + igt_assert_eq(__gem_execbuf(fd, &execbuf), -EINVAL); + return; + } + + /* If we do allow the wait on a future fence, it should autoexpire */ + gem_sync(fd, obj.handle); + out = execbuf.rsvd2 >> 32; + igt_assert_eq(sync_fence_status(out), -ETIMEDOUT); + close(out); gem_close(fd, obj.handle); syncobj_destroy(fd, fence.handle); } +static void test_syncobj_incomplete_wait_submit(int i915) +{ + struct drm_i915_gem_exec_object2 obj = { + .handle = batch_create(i915), + }; + struct drm_i915_gem_exec_fence fence = { + .handle = syncobj_create(i915, 0), + .flags = I915_EXEC_FENCE_WAIT | I915_EXEC_FENCE_WAIT_SUBMIT, + }; + struct drm_i915_gem_execbuffer2 execbuf = { + .buffers_ptr = to_user_pointer(&obj), + .buffer_count = 1, + + .cliprects_ptr = to_user_pointer(&fence), + .num_cliprects = 1, + + .flags = I915_EXEC_FENCE_ARRAY | I915_EXEC_FENCE_OUT, + }; + int out; + + /* waiting before the fence is set is^W may be invalid */ + if (__gem_execbuf_wr(i915, &execbuf)) { + igt_assert_eq(__gem_execbuf(i915, &execbuf), -EINVAL); + return; + } + + /* If we do allow the wait on a future fence, it should autoexpire */ + gem_sync(i915, obj.handle); + out = execbuf.rsvd2 >> 32; + igt_assert_eq(sync_fence_status(out), -ETIMEDOUT); + close(out); + + gem_close(i915, obj.handle); + syncobj_destroy(i915, fence.handle); +} + static void test_syncobj_invalid_flags(int fd) { const uint32_t bbe = MI_BATCH_BUFFER_END; @@ -1289,6 +1335,517 @@ static void test_syncobj_wait(int fd) } } +static uint32_t future_batch(int i915, uint32_t offset) +{ + uint32_t handle = gem_create(i915, 4096); + const int gen = intel_gen(intel_get_drm_devid(i915)); + uint32_t cs[16]; + int i = 0; + + cs[i] = MI_STORE_DWORD_IMM | (gen < 6 ? 1 << 22 : 0); + if (gen >= 8) { + cs[++i] = offset + 4000; + cs[++i] = 0; + } else if (gen >= 4) { + cs[++i] = 0; + cs[++i] = offset + 4000; + } else { + cs[i]--; + cs[++i] = offset + 4000; + } + cs[++i] = 1; + cs[i + 1] = MI_BATCH_BUFFER_END; + gem_write(i915, handle, 0, cs, sizeof(cs)); + + cs[i] = 2; + gem_write(i915, handle, 64, cs, sizeof(cs)); + + return handle; +} + +static void test_syncobj_future(int i915, unsigned int engine) +{ + struct drm_i915_gem_exec_object2 obj = { + .offset = 24 << 20, + .handle = future_batch(i915, 24 << 20), + .flags = EXEC_OBJECT_PINNED, + }; + struct drm_i915_gem_exec_fence fence = {}; + struct drm_i915_gem_execbuffer2 execbuf = { + .buffers_ptr = to_user_pointer(&obj), + .buffer_count = 1, + .cliprects_ptr = to_user_pointer(&fence), + .num_cliprects = 1, + }; + const struct intel_execution_engine2 *e; + + /* + * Client A is waiting on a future fence from B. So even though its + * execbuf is called first, we need to hold it in a queue waiting on + * B. + */ + igt_require(gem_scheduler_enabled(i915)); + + __for_each_physical_engine(i915, e) { + uint32_t result; + + igt_debug("waiting on future %s\n", e->name); + fence.handle = syncobj_create(i915, 0); + + fence.flags = I915_EXEC_FENCE_WAIT; + execbuf.batch_start_offset = 0; + execbuf.flags = engine | I915_EXEC_FENCE_ARRAY; + execbuf.rsvd1 = 0; + gem_execbuf(i915, &execbuf); /* writes 1 */ + + fence.flags = I915_EXEC_FENCE_SIGNAL; + execbuf.batch_start_offset = 64; + execbuf.flags = e->flags | I915_EXEC_FENCE_ARRAY; + execbuf.rsvd1 = gem_context_clone_with_engines(i915, 0); + gem_execbuf(i915, &execbuf); /* writes 2 */ + gem_context_destroy(i915, execbuf.rsvd1); + + syncobj_destroy(i915, fence.handle); + gem_sync(i915, obj.handle); /* write hazard lies */ + gem_read(i915, obj.handle, 4000, &result, sizeof(result)); + igt_assert_eq(result, 1); + } + + gem_close(i915, obj.handle); +} + +static uint32_t future_submit_batch(int i915, uint32_t offset) +{ + uint32_t handle = gem_create(i915, 4096); + const int gen = intel_gen(intel_get_drm_devid(i915)); + uint32_t cs[16]; + int i = 0; + + cs[i] = MI_STORE_DWORD_IMM | (gen < 6 ? 1 << 22 : 0); + if (gen >= 8) { + cs[++i] = offset + 4000; + cs[++i] = 0; + } else if (gen >= 4) { + cs[++i] = 0; + cs[++i] = offset + 4000; + } else { + cs[i]--; + cs[++i] = offset + 4000; + } + cs[++i] = 1; + cs[i + 1] = MI_BATCH_BUFFER_END; + igt_assert(i + 1 < ARRAY_SIZE(cs)); + gem_write(i915, handle, 0, cs, sizeof(cs)); + + i = 0; + cs[i++] = + MI_SEMAPHORE_WAIT | + MI_SEMAPHORE_POLL | + MI_SEMAPHORE_SAD_EQ_SDD | + (4 - 2); + cs[i++] = 1; + cs[i++] = offset + 4000; + cs[i++] = 0; + + cs[i] = MI_STORE_DWORD_IMM | (gen < 6 ? 1 << 22 : 0); + if (gen >= 8) { + cs[++i] = offset + 4000; + cs[++i] = 0; + } else if (gen >= 4) { + cs[++i] = 0; + cs[++i] = offset + 4000; + } else { + cs[i]--; + cs[++i] = offset + 4000; + } + cs[++i] = 2; + cs[++i] = MI_BATCH_BUFFER_END; + igt_assert(i < ARRAY_SIZE(cs)); + + gem_write(i915, handle, 64, cs, sizeof(cs)); + + return handle; +} + +static void test_syncobj_future_submit(int i915, unsigned int engine) +{ + struct drm_i915_gem_exec_object2 obj = { + .offset = 24 << 20, + .handle = future_submit_batch(i915, 24 << 20), + .flags = EXEC_OBJECT_PINNED, + }; + const struct intel_execution_engine2 *e; + + /* + * Here we submit client A waiting on client B, but internally client + * B has a semaphore that waits on client A. This relies on timeslicing + * to reorder B before A, even though userspace has asked to submit + * A & B simultaneously (and due to the sequence we will submit B + * then A). + */ + igt_require(gem_scheduler_has_timeslicing(i915)); + + __for_each_physical_engine(i915, e) { + struct drm_i915_gem_exec_fence fence = { + .handle = syncobj_create(i915, 0), + }; + struct drm_i915_gem_execbuffer2 execbuf = { + .buffers_ptr = to_user_pointer(&obj), + .buffer_count = 1, + .cliprects_ptr = to_user_pointer(&fence), + .num_cliprects = 1, + .flags = engine | I915_EXEC_FENCE_ARRAY, + }; + uint32_t result; + int out; + + igt_debug("waiting on future %s\n", e->name); + + execbuf.rsvd1 = gem_context_clone_with_engines(i915, 0); + fence.flags = I915_EXEC_FENCE_WAIT | I915_EXEC_FENCE_WAIT_SUBMIT; + execbuf.batch_start_offset = 0; + execbuf.flags |= I915_EXEC_FENCE_OUT; + igt_require(__gem_execbuf_wr(i915, &execbuf) == 0); /* writes 1 */ + execbuf.flags &= ~I915_EXEC_FENCE_OUT; + gem_context_destroy(i915, execbuf.rsvd1); + + execbuf.rsvd1 = gem_context_clone_with_engines(i915, 0); + fence.flags = I915_EXEC_FENCE_SIGNAL; + execbuf.batch_start_offset = 64; + execbuf.flags &= ~I915_EXEC_RING_MASK; + execbuf.flags |= e->flags; + gem_execbuf(i915, &execbuf); /* writes 2 */ + gem_context_destroy(i915, execbuf.rsvd1); + + syncobj_destroy(i915, fence.handle); + gem_sync(i915, obj.handle); /* write hazard lies */ + gem_read(i915, obj.handle, 4000, &result, sizeof(result)); + igt_assert_eq(result, 2); + + /* check we didn't autotimeout */ + out = execbuf.rsvd2 >> 32; + igt_assert_eq(sync_fence_status(out), 1); + close(out); + } + + gem_close(i915, obj.handle); +} + +static void test_syncobj_future_past(int i915, unsigned int engine, int flags) +{ + struct drm_i915_gem_exec_object2 obj = { + .offset = 24 << 20, + .handle = future_batch(i915, 24 << 20), + .flags = EXEC_OBJECT_PINNED, + }; + struct drm_i915_gem_exec_fence fence = { + .handle = syncobj_create(i915, 0), + }; + struct drm_i915_gem_execbuffer2 execbuf = { + .buffers_ptr = to_user_pointer(&obj), + .buffer_count = 1, + .cliprects_ptr = to_user_pointer(&fence), + .num_cliprects = 1, + .flags = engine | I915_EXEC_FENCE_ARRAY, + }; + uint32_t result; + int out; + + fence.flags = I915_EXEC_FENCE_WAIT | I915_EXEC_FENCE_SIGNAL | flags; + execbuf.batch_start_offset = 0; + execbuf.flags |= I915_EXEC_FENCE_OUT; + igt_require(__gem_execbuf_wr(i915, &execbuf) == 0); /* writes 1 */ + execbuf.flags &= ~I915_EXEC_FENCE_OUT; + + gem_sync(i915, obj.handle); /* write hazard lies */ + gem_read(i915, obj.handle, 4000, &result, sizeof(result)); + igt_assert_eq(result, 1); + + /* check we didn't autotimeout */ + out = execbuf.rsvd2 >> 32; + igt_assert_eq(sync_fence_status(out), 1); + close(out); + + gem_close(i915, obj.handle); + syncobj_destroy(i915, fence.handle); +} + +static void test_syncobj_future_self(int i915, unsigned int engine, int flags) +{ + struct drm_i915_gem_exec_object2 obj = { + .offset = 24 << 20, + .handle = future_batch(i915, 24 << 20), + .flags = EXEC_OBJECT_PINNED, + }; + struct drm_i915_gem_exec_fence fence = { + .handle = syncobj_create(i915, 0), + }; + struct drm_i915_gem_execbuffer2 execbuf = { + .buffers_ptr = to_user_pointer(&obj), + .buffer_count = 1, + .cliprects_ptr = to_user_pointer(&fence), + .num_cliprects = 1, + .flags = engine | I915_EXEC_FENCE_ARRAY, + }; + int out; + + fence.flags = I915_EXEC_FENCE_WAIT | flags; + execbuf.batch_start_offset = 0; + execbuf.flags |= I915_EXEC_FENCE_OUT; + igt_require(__gem_execbuf_wr(i915, &execbuf) == 0); /* writes 1 */ + execbuf.flags &= ~I915_EXEC_FENCE_OUT; + + fence.flags = I915_EXEC_FENCE_SIGNAL; + execbuf.batch_start_offset = 64; + gem_execbuf(i915, &execbuf); /* writes 2 */ + + gem_sync(i915, obj.handle); /* write hazard lies */ + + gem_close(i915, obj.handle); + syncobj_destroy(i915, fence.handle); + + out = execbuf.rsvd2 >> 32; + igt_assert_eq(sync_fence_status(out), -EDEADLK); + close(out); +} + +static void test_syncobj_future_pair(int i915, unsigned int engine) +{ + struct drm_i915_gem_exec_object2 obj = { + .offset = 24 << 20, + .handle = submitN_batches(i915, 24 << 20, 2), + .flags = EXEC_OBJECT_PINNED, + }; + struct drm_i915_gem_exec_fence fence[2] = { + { .handle = syncobj_create(i915, 0) }, + { .handle = syncobj_create(i915, 0) } + }; + struct drm_i915_gem_execbuffer2 execbuf = { + .buffers_ptr = to_user_pointer(&obj), + .buffer_count = 1, + .cliprects_ptr = to_user_pointer(fence), + .num_cliprects = 2, + }; + const struct intel_execution_engine2 *e; + + __for_each_physical_engine(i915, e) { + int out = 0; + + gem_write(i915, obj.handle, 0, &out, sizeof(out)); + fence[0].handle = syncobj_create(i915, 0); + fence[1].handle = syncobj_create(i915, 0); + + fence[0].flags = I915_EXEC_FENCE_SIGNAL; + fence[1].flags = + I915_EXEC_FENCE_WAIT | I915_EXEC_FENCE_WAIT_SUBMIT; + execbuf.batch_start_offset = 1024; + execbuf.flags = + engine | I915_EXEC_FENCE_ARRAY | I915_EXEC_FENCE_OUT; + execbuf.rsvd1 = gem_context_create(i915); + igt_require(__gem_execbuf_wr(i915, &execbuf) == 0); + gem_context_destroy(i915, execbuf.rsvd1); + execbuf.rsvd2 >>= 32; + + fence[0].flags = + I915_EXEC_FENCE_WAIT | I915_EXEC_FENCE_WAIT_SUBMIT; + fence[1].flags = I915_EXEC_FENCE_SIGNAL; + execbuf.batch_start_offset = 2048; + execbuf.flags = + e->flags | I915_EXEC_FENCE_ARRAY | I915_EXEC_FENCE_OUT; + execbuf.rsvd1 = gem_context_create(i915); + gem_execbuf_wr(i915, &execbuf); + gem_context_destroy(i915, execbuf.rsvd1); + + syncobj_destroy(i915, fence[0].handle); + syncobj_destroy(i915, fence[1].handle); + + gem_sync(i915, obj.handle); /* write hazard lies */ + + out = execbuf.rsvd2; + igt_assert_eq(sync_fence_status(out), 1); + close(out); + + out = execbuf.rsvd2 >> 32; + igt_assert_eq(sync_fence_status(out), 1); + close(out); + + gem_read(i915, obj.handle, 0, &out, sizeof(out)); + igt_assert_eq(out, 16); + } + + gem_close(i915, obj.handle); +} + +static void test_syncobj_future_group(int i915, unsigned int engine, int count) +{ + struct drm_i915_gem_exec_object2 obj = { + .offset = 24 << 20, + .handle = submitN_batches(i915, 24 << 20, count), + .flags = EXEC_OBJECT_PINNED, + }; + struct drm_i915_gem_exec_fence fence[count]; + struct drm_i915_gem_execbuffer2 execbuf = { + .buffers_ptr = to_user_pointer(&obj), + .buffer_count = 1, + .cliprects_ptr = to_user_pointer(fence), + .num_cliprects = count, + .flags = engine | I915_EXEC_FENCE_ARRAY | I915_EXEC_FENCE_OUT, + }; + int out[count]; + uint32_t result; + + for (int i = 0; i < count; i++) { + fence[i].handle = syncobj_create(i915, 0); + fence[i].flags = + I915_EXEC_FENCE_WAIT | I915_EXEC_FENCE_WAIT_SUBMIT; + } + + for (int i = 0; i < count; i++) { + fence[i].flags = I915_EXEC_FENCE_SIGNAL; + + execbuf.batch_start_offset = 1024 * (i + 1); + execbuf.rsvd1 = gem_context_create(i915); + gem_execbuf_wr(i915, &execbuf); + gem_context_destroy(i915, execbuf.rsvd1); + + out[i] = execbuf.rsvd2 >> 32; + fence[i].flags = + I915_EXEC_FENCE_WAIT | I915_EXEC_FENCE_WAIT_SUBMIT; + } + gem_sync(i915, obj.handle); /* write hazard lies */ + + /* As both batches were waiting for the other to start -- deadlock? */ + for (int i = 0; i < count; i++) { + syncobj_destroy(i915, fence[i].handle); + igt_assert_eq(sync_fence_status(out[i]), 1); + close(out[i]); + } + + /* Nevertheless, we ignored^Wresolved the deadlock and let them run */ + gem_read(i915, obj.handle, 0, &result, sizeof(result)); + igt_assert_eq(result, 8 * count); + gem_close(i915, obj.handle); +} + + +static void +test_syncobj_future_deadlock(int i915, unsigned int engine, int flags) +{ + struct drm_i915_gem_exec_object2 obj = { + .offset = 24 << 20, + .handle = future_batch(i915, 24 << 20), + .flags = EXEC_OBJECT_PINNED, + }; + struct drm_i915_gem_exec_fence fence = {}; + struct drm_i915_gem_execbuffer2 execbuf = { + .buffers_ptr = to_user_pointer(&obj), + .buffer_count = 1, + .cliprects_ptr = to_user_pointer(&fence), + .num_cliprects = 1, + }; + const struct intel_execution_engine2 *e; + + __for_each_physical_engine(i915, e) { + int out; + + fence.handle = syncobj_create(i915, 0), + + fence.flags = I915_EXEC_FENCE_WAIT | flags; + execbuf.batch_start_offset = 0; + execbuf.flags = engine | I915_EXEC_FENCE_ARRAY, + execbuf.flags |= I915_EXEC_FENCE_OUT; + execbuf.rsvd1 = 0; + gem_execbuf_wr(i915, &execbuf); /* writes 1 */ + + fence.flags = I915_EXEC_FENCE_SIGNAL; + execbuf.batch_start_offset = 64; + execbuf.flags = e->flags | I915_EXEC_FENCE_ARRAY, + execbuf.flags |= I915_EXEC_FENCE_OUT | I915_EXEC_FENCE_IN; + execbuf.rsvd1 = gem_context_clone_with_engines(i915, 0); + execbuf.rsvd2 >>= 32; + gem_execbuf_wr(i915, &execbuf); /* writes 2 */ + gem_context_destroy(i915, execbuf.rsvd1); + + syncobj_destroy(i915, fence.handle); + gem_sync(i915, obj.handle); + + /* How should this deadlock be resolved? */ + out = execbuf.rsvd2; + igt_assert_eq(sync_fence_status(out), -EDEADLK); + close(out); + + out = execbuf.rsvd2 >> 32; + igt_assert_eq(sync_fence_status(out), -EDEADLK); + close(out); + } + + gem_close(i915, obj.handle); +} + +static void +test_syncobj_future_cycle(int i915, unsigned int engine, int flags) +{ + struct drm_i915_gem_exec_object2 obj = { + .offset = 24 << 20, + .handle = future_batch(i915, 24 << 20), + .flags = EXEC_OBJECT_PINNED, + }; + const struct intel_execution_engine2 *e1, *e2; + + __for_each_physical_engine(i915, e1) { + __for_each_physical_engine(i915, e2) { + struct drm_i915_gem_exec_fence fence = { + .handle = syncobj_create(i915, 0), + }; + struct drm_i915_gem_execbuffer2 execbuf = { + .buffers_ptr = to_user_pointer(&obj), + .buffer_count = 1, + .cliprects_ptr = to_user_pointer(&fence), + .num_cliprects = 1, + .flags = engine | I915_EXEC_FENCE_ARRAY, + }; + int out; + + fence.flags = I915_EXEC_FENCE_WAIT | flags; + execbuf.batch_start_offset = 0; + execbuf.flags |= I915_EXEC_FENCE_OUT; + igt_require(__gem_execbuf_wr(i915, &execbuf) == 0); + + fence.flags = 0; + execbuf.rsvd1 = gem_context_clone_with_engines(i915, 0); + execbuf.rsvd2 >>= 32; + execbuf.flags &= ~I915_EXEC_RING_MASK; + execbuf.flags |= e1->flags | I915_EXEC_FENCE_IN; + gem_execbuf_wr(i915, &execbuf); + gem_context_destroy(i915, execbuf.rsvd1); + close(execbuf.rsvd2); + + fence.flags = I915_EXEC_FENCE_SIGNAL; + execbuf.rsvd1 = gem_context_clone_with_engines(i915, 0); + execbuf.rsvd2 >>= 32; + execbuf.flags &= ~I915_EXEC_RING_MASK; + execbuf.flags |= e2->flags; + execbuf.batch_start_offset = 64; + gem_execbuf_wr(i915, &execbuf); /* writes 2 */ + gem_context_destroy(i915, execbuf.rsvd1); + + syncobj_destroy(i915, fence.handle); + gem_sync(i915, obj.handle); + + + /* How should this deadlock be resolved? */ + out = execbuf.rsvd2 >> 32; + igt_assert_eq(sync_fence_status(out), -EDEADLK); + close(out); + + out = execbuf.rsvd2; + igt_assert_eq(sync_fence_status(out), -EDEADLK); + close(out); + }} + + gem_close(i915, obj.handle); +} + static void test_syncobj_export(int fd) { const uint32_t bbe = MI_BATCH_BUFFER_END; @@ -1574,6 +2131,117 @@ static void test_syncobj_channel(int fd) syncobj_destroy(fd, syncobj[i]); } +static bool has_future_syncobj(int i915) +{ + struct drm_i915_gem_exec_object2 obj = { + .handle = batch_create(i915), + }; + struct drm_i915_gem_exec_fence fence = { + .handle = syncobj_create(i915, 0), + .flags = I915_EXEC_FENCE_WAIT | I915_EXEC_FENCE_SIGNAL, + }; + struct drm_i915_gem_execbuffer2 execbuf = { + .buffers_ptr = to_user_pointer(&obj), + .buffer_count = 1, + .cliprects_ptr = to_user_pointer(&fence), + .num_cliprects = 1, + .flags = I915_EXEC_FENCE_ARRAY, + }; + bool result; + + result = __gem_execbuf(i915, &execbuf) == 0; + gem_close(i915, obj.handle); + + return result; +} + +static void syncobj_futures(int i915) +{ + const struct intel_execution_engine2 *e; + + igt_fixture { + igt_require(gem_scheduler_enabled(i915)); + igt_require(has_future_syncobj(i915)); + } + + igt_subtest_with_dynamic("syncobj-future") + __for_each_physical_engine(i915, e) { + igt_dynamic_f("%s", e->name) + test_syncobj_future(i915, e->flags); + } + + igt_subtest_with_dynamic("syncobj-future-past") + __for_each_physical_engine(i915, e) { + igt_dynamic_f("%s", e->name) + test_syncobj_future_past(i915, e->flags, 0); + } + + + igt_subtest_with_dynamic("syncobj-future-submit") + __for_each_physical_engine(i915, e) { + igt_dynamic_f("%s", e->name) + test_syncobj_future_submit(i915, e->flags); + } + + igt_subtest_with_dynamic("syncobj-future-submit-past") + __for_each_physical_engine(i915, e) { + igt_dynamic_f("%s", e->name) + test_syncobj_future_past(i915, e->flags, + I915_EXEC_FENCE_WAIT_SUBMIT); + } + + igt_subtest_with_dynamic("syncobj-future-self") + __for_each_physical_engine(i915, e) { + igt_dynamic_f("%s", e->name) + test_syncobj_future_self(i915, e->flags, 0); + } + + igt_subtest_with_dynamic("syncobj-future-self-submit") + __for_each_physical_engine(i915, e) { + igt_dynamic_f("%s", e->name) + test_syncobj_future_self(i915, e->flags, + I915_EXEC_FENCE_WAIT_SUBMIT); + } + + igt_subtest_with_dynamic("syncobj-future-pair") + __for_each_physical_engine(i915, e) { + igt_dynamic_f("%s", e->name) + test_syncobj_future_pair(i915, e->flags); + } + + igt_subtest_with_dynamic("syncobj-future-group") + __for_each_physical_engine(i915, e) { + igt_dynamic_f("%s", e->name) + test_syncobj_future_group(i915, e->flags, 67); + } + + igt_subtest_with_dynamic("syncobj-future-deadlock") + __for_each_physical_engine(i915, e) { + igt_dynamic_f("%s", e->name) + test_syncobj_future_deadlock(i915, e->flags, 0); + } + + igt_subtest_with_dynamic("syncobj-future-submit-deadlock") + __for_each_physical_engine(i915, e) { + igt_dynamic_f("%s", e->name) + test_syncobj_future_deadlock(i915, e->flags, + I915_EXEC_FENCE_WAIT_SUBMIT); + } + + igt_subtest_with_dynamic("syncobj-future-cycle") + __for_each_physical_engine(i915, e) { + igt_dynamic_f("%s", e->name) + test_syncobj_future_cycle(i915, e->flags, 0); + } + + igt_subtest_with_dynamic("syncobj-future-submit-cycle") + __for_each_physical_engine(i915, e) { + igt_dynamic_f("%s", e->name) + test_syncobj_future_cycle(i915, e->flags, + I915_EXEC_FENCE_WAIT_SUBMIT); + } +} + igt_main { const struct intel_execution_engine2 *e; @@ -1786,6 +2454,9 @@ igt_main igt_subtest("syncobj-invalid-wait") test_syncobj_invalid_wait(i915); + igt_subtest("syncobj-incomplete-wait-submit") + test_syncobj_incomplete_wait_submit(i915); + igt_subtest("syncobj-invalid-flags") test_syncobj_invalid_flags(i915); @@ -1795,6 +2466,9 @@ igt_main igt_subtest("syncobj-wait") test_syncobj_wait(i915); + igt_subtest_group + syncobj_futures(i915); + igt_subtest("syncobj-export") test_syncobj_export(i915); -- 2.26.2 _______________________________________________ Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
WARNING: multiple messages have this Message-ID (diff)
From: Chris Wilson <chris@chris-wilson.co.uk> To: intel-gfx@lists.freedesktop.org Cc: igt-dev@lists.freedesktop.org, Chris Wilson <chris@chris-wilson.co.uk> Subject: [igt-dev] [PATCH i-g-t 2/2] i915/gem_exec_fence: Teach invalid-wait about invalid future fences Date: Wed, 13 May 2020 18:02:24 +0100 [thread overview] Message-ID: <20200513170224.848726-2-chris@chris-wilson.co.uk> (raw) In-Reply-To: <20200513170224.848726-1-chris@chris-wilson.co.uk> When we allow a wait on a future future fence, it must autoexpire if the fence is never signaled by userspace. Also put future fences to work, as the intention is to use them, along with WAIT_SUBMIT and semaphores, for userspace to perform its own fine-grained scheduling. Or simply run concurrent clients without having to flush batches between context switches. v2: Verify deadlock detection Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> --- tests/i915/gem_exec_fence.c | 680 +++++++++++++++++++++++++++++++++++- 1 file changed, 677 insertions(+), 3 deletions(-) diff --git a/tests/i915/gem_exec_fence.c b/tests/i915/gem_exec_fence.c index 4140bff24..eb1165080 100644 --- a/tests/i915/gem_exec_fence.c +++ b/tests/i915/gem_exec_fence.c @@ -1123,11 +1123,12 @@ static void test_syncobj_invalid_wait(int fd) struct drm_i915_gem_exec_fence fence = { .handle = syncobj_create(fd, 0), }; + int out; memset(&execbuf, 0, sizeof(execbuf)); execbuf.buffers_ptr = to_user_pointer(&obj); execbuf.buffer_count = 1; - execbuf.flags = I915_EXEC_FENCE_ARRAY; + execbuf.flags = I915_EXEC_FENCE_ARRAY | I915_EXEC_FENCE_OUT; execbuf.cliprects_ptr = to_user_pointer(&fence); execbuf.num_cliprects = 1; @@ -1135,14 +1136,59 @@ static void test_syncobj_invalid_wait(int fd) obj.handle = gem_create(fd, 4096); gem_write(fd, obj.handle, 0, &bbe, sizeof(bbe)); - /* waiting before the fence is set is invalid */ + /* waiting before the fence is set is^W may be invalid */ fence.flags = I915_EXEC_FENCE_WAIT; - igt_assert_eq(__gem_execbuf(fd, &execbuf), -EINVAL); + if (__gem_execbuf_wr(fd, &execbuf)) { + igt_assert_eq(__gem_execbuf(fd, &execbuf), -EINVAL); + return; + } + + /* If we do allow the wait on a future fence, it should autoexpire */ + gem_sync(fd, obj.handle); + out = execbuf.rsvd2 >> 32; + igt_assert_eq(sync_fence_status(out), -ETIMEDOUT); + close(out); gem_close(fd, obj.handle); syncobj_destroy(fd, fence.handle); } +static void test_syncobj_incomplete_wait_submit(int i915) +{ + struct drm_i915_gem_exec_object2 obj = { + .handle = batch_create(i915), + }; + struct drm_i915_gem_exec_fence fence = { + .handle = syncobj_create(i915, 0), + .flags = I915_EXEC_FENCE_WAIT | I915_EXEC_FENCE_WAIT_SUBMIT, + }; + struct drm_i915_gem_execbuffer2 execbuf = { + .buffers_ptr = to_user_pointer(&obj), + .buffer_count = 1, + + .cliprects_ptr = to_user_pointer(&fence), + .num_cliprects = 1, + + .flags = I915_EXEC_FENCE_ARRAY | I915_EXEC_FENCE_OUT, + }; + int out; + + /* waiting before the fence is set is^W may be invalid */ + if (__gem_execbuf_wr(i915, &execbuf)) { + igt_assert_eq(__gem_execbuf(i915, &execbuf), -EINVAL); + return; + } + + /* If we do allow the wait on a future fence, it should autoexpire */ + gem_sync(i915, obj.handle); + out = execbuf.rsvd2 >> 32; + igt_assert_eq(sync_fence_status(out), -ETIMEDOUT); + close(out); + + gem_close(i915, obj.handle); + syncobj_destroy(i915, fence.handle); +} + static void test_syncobj_invalid_flags(int fd) { const uint32_t bbe = MI_BATCH_BUFFER_END; @@ -1289,6 +1335,517 @@ static void test_syncobj_wait(int fd) } } +static uint32_t future_batch(int i915, uint32_t offset) +{ + uint32_t handle = gem_create(i915, 4096); + const int gen = intel_gen(intel_get_drm_devid(i915)); + uint32_t cs[16]; + int i = 0; + + cs[i] = MI_STORE_DWORD_IMM | (gen < 6 ? 1 << 22 : 0); + if (gen >= 8) { + cs[++i] = offset + 4000; + cs[++i] = 0; + } else if (gen >= 4) { + cs[++i] = 0; + cs[++i] = offset + 4000; + } else { + cs[i]--; + cs[++i] = offset + 4000; + } + cs[++i] = 1; + cs[i + 1] = MI_BATCH_BUFFER_END; + gem_write(i915, handle, 0, cs, sizeof(cs)); + + cs[i] = 2; + gem_write(i915, handle, 64, cs, sizeof(cs)); + + return handle; +} + +static void test_syncobj_future(int i915, unsigned int engine) +{ + struct drm_i915_gem_exec_object2 obj = { + .offset = 24 << 20, + .handle = future_batch(i915, 24 << 20), + .flags = EXEC_OBJECT_PINNED, + }; + struct drm_i915_gem_exec_fence fence = {}; + struct drm_i915_gem_execbuffer2 execbuf = { + .buffers_ptr = to_user_pointer(&obj), + .buffer_count = 1, + .cliprects_ptr = to_user_pointer(&fence), + .num_cliprects = 1, + }; + const struct intel_execution_engine2 *e; + + /* + * Client A is waiting on a future fence from B. So even though its + * execbuf is called first, we need to hold it in a queue waiting on + * B. + */ + igt_require(gem_scheduler_enabled(i915)); + + __for_each_physical_engine(i915, e) { + uint32_t result; + + igt_debug("waiting on future %s\n", e->name); + fence.handle = syncobj_create(i915, 0); + + fence.flags = I915_EXEC_FENCE_WAIT; + execbuf.batch_start_offset = 0; + execbuf.flags = engine | I915_EXEC_FENCE_ARRAY; + execbuf.rsvd1 = 0; + gem_execbuf(i915, &execbuf); /* writes 1 */ + + fence.flags = I915_EXEC_FENCE_SIGNAL; + execbuf.batch_start_offset = 64; + execbuf.flags = e->flags | I915_EXEC_FENCE_ARRAY; + execbuf.rsvd1 = gem_context_clone_with_engines(i915, 0); + gem_execbuf(i915, &execbuf); /* writes 2 */ + gem_context_destroy(i915, execbuf.rsvd1); + + syncobj_destroy(i915, fence.handle); + gem_sync(i915, obj.handle); /* write hazard lies */ + gem_read(i915, obj.handle, 4000, &result, sizeof(result)); + igt_assert_eq(result, 1); + } + + gem_close(i915, obj.handle); +} + +static uint32_t future_submit_batch(int i915, uint32_t offset) +{ + uint32_t handle = gem_create(i915, 4096); + const int gen = intel_gen(intel_get_drm_devid(i915)); + uint32_t cs[16]; + int i = 0; + + cs[i] = MI_STORE_DWORD_IMM | (gen < 6 ? 1 << 22 : 0); + if (gen >= 8) { + cs[++i] = offset + 4000; + cs[++i] = 0; + } else if (gen >= 4) { + cs[++i] = 0; + cs[++i] = offset + 4000; + } else { + cs[i]--; + cs[++i] = offset + 4000; + } + cs[++i] = 1; + cs[i + 1] = MI_BATCH_BUFFER_END; + igt_assert(i + 1 < ARRAY_SIZE(cs)); + gem_write(i915, handle, 0, cs, sizeof(cs)); + + i = 0; + cs[i++] = + MI_SEMAPHORE_WAIT | + MI_SEMAPHORE_POLL | + MI_SEMAPHORE_SAD_EQ_SDD | + (4 - 2); + cs[i++] = 1; + cs[i++] = offset + 4000; + cs[i++] = 0; + + cs[i] = MI_STORE_DWORD_IMM | (gen < 6 ? 1 << 22 : 0); + if (gen >= 8) { + cs[++i] = offset + 4000; + cs[++i] = 0; + } else if (gen >= 4) { + cs[++i] = 0; + cs[++i] = offset + 4000; + } else { + cs[i]--; + cs[++i] = offset + 4000; + } + cs[++i] = 2; + cs[++i] = MI_BATCH_BUFFER_END; + igt_assert(i < ARRAY_SIZE(cs)); + + gem_write(i915, handle, 64, cs, sizeof(cs)); + + return handle; +} + +static void test_syncobj_future_submit(int i915, unsigned int engine) +{ + struct drm_i915_gem_exec_object2 obj = { + .offset = 24 << 20, + .handle = future_submit_batch(i915, 24 << 20), + .flags = EXEC_OBJECT_PINNED, + }; + const struct intel_execution_engine2 *e; + + /* + * Here we submit client A waiting on client B, but internally client + * B has a semaphore that waits on client A. This relies on timeslicing + * to reorder B before A, even though userspace has asked to submit + * A & B simultaneously (and due to the sequence we will submit B + * then A). + */ + igt_require(gem_scheduler_has_timeslicing(i915)); + + __for_each_physical_engine(i915, e) { + struct drm_i915_gem_exec_fence fence = { + .handle = syncobj_create(i915, 0), + }; + struct drm_i915_gem_execbuffer2 execbuf = { + .buffers_ptr = to_user_pointer(&obj), + .buffer_count = 1, + .cliprects_ptr = to_user_pointer(&fence), + .num_cliprects = 1, + .flags = engine | I915_EXEC_FENCE_ARRAY, + }; + uint32_t result; + int out; + + igt_debug("waiting on future %s\n", e->name); + + execbuf.rsvd1 = gem_context_clone_with_engines(i915, 0); + fence.flags = I915_EXEC_FENCE_WAIT | I915_EXEC_FENCE_WAIT_SUBMIT; + execbuf.batch_start_offset = 0; + execbuf.flags |= I915_EXEC_FENCE_OUT; + igt_require(__gem_execbuf_wr(i915, &execbuf) == 0); /* writes 1 */ + execbuf.flags &= ~I915_EXEC_FENCE_OUT; + gem_context_destroy(i915, execbuf.rsvd1); + + execbuf.rsvd1 = gem_context_clone_with_engines(i915, 0); + fence.flags = I915_EXEC_FENCE_SIGNAL; + execbuf.batch_start_offset = 64; + execbuf.flags &= ~I915_EXEC_RING_MASK; + execbuf.flags |= e->flags; + gem_execbuf(i915, &execbuf); /* writes 2 */ + gem_context_destroy(i915, execbuf.rsvd1); + + syncobj_destroy(i915, fence.handle); + gem_sync(i915, obj.handle); /* write hazard lies */ + gem_read(i915, obj.handle, 4000, &result, sizeof(result)); + igt_assert_eq(result, 2); + + /* check we didn't autotimeout */ + out = execbuf.rsvd2 >> 32; + igt_assert_eq(sync_fence_status(out), 1); + close(out); + } + + gem_close(i915, obj.handle); +} + +static void test_syncobj_future_past(int i915, unsigned int engine, int flags) +{ + struct drm_i915_gem_exec_object2 obj = { + .offset = 24 << 20, + .handle = future_batch(i915, 24 << 20), + .flags = EXEC_OBJECT_PINNED, + }; + struct drm_i915_gem_exec_fence fence = { + .handle = syncobj_create(i915, 0), + }; + struct drm_i915_gem_execbuffer2 execbuf = { + .buffers_ptr = to_user_pointer(&obj), + .buffer_count = 1, + .cliprects_ptr = to_user_pointer(&fence), + .num_cliprects = 1, + .flags = engine | I915_EXEC_FENCE_ARRAY, + }; + uint32_t result; + int out; + + fence.flags = I915_EXEC_FENCE_WAIT | I915_EXEC_FENCE_SIGNAL | flags; + execbuf.batch_start_offset = 0; + execbuf.flags |= I915_EXEC_FENCE_OUT; + igt_require(__gem_execbuf_wr(i915, &execbuf) == 0); /* writes 1 */ + execbuf.flags &= ~I915_EXEC_FENCE_OUT; + + gem_sync(i915, obj.handle); /* write hazard lies */ + gem_read(i915, obj.handle, 4000, &result, sizeof(result)); + igt_assert_eq(result, 1); + + /* check we didn't autotimeout */ + out = execbuf.rsvd2 >> 32; + igt_assert_eq(sync_fence_status(out), 1); + close(out); + + gem_close(i915, obj.handle); + syncobj_destroy(i915, fence.handle); +} + +static void test_syncobj_future_self(int i915, unsigned int engine, int flags) +{ + struct drm_i915_gem_exec_object2 obj = { + .offset = 24 << 20, + .handle = future_batch(i915, 24 << 20), + .flags = EXEC_OBJECT_PINNED, + }; + struct drm_i915_gem_exec_fence fence = { + .handle = syncobj_create(i915, 0), + }; + struct drm_i915_gem_execbuffer2 execbuf = { + .buffers_ptr = to_user_pointer(&obj), + .buffer_count = 1, + .cliprects_ptr = to_user_pointer(&fence), + .num_cliprects = 1, + .flags = engine | I915_EXEC_FENCE_ARRAY, + }; + int out; + + fence.flags = I915_EXEC_FENCE_WAIT | flags; + execbuf.batch_start_offset = 0; + execbuf.flags |= I915_EXEC_FENCE_OUT; + igt_require(__gem_execbuf_wr(i915, &execbuf) == 0); /* writes 1 */ + execbuf.flags &= ~I915_EXEC_FENCE_OUT; + + fence.flags = I915_EXEC_FENCE_SIGNAL; + execbuf.batch_start_offset = 64; + gem_execbuf(i915, &execbuf); /* writes 2 */ + + gem_sync(i915, obj.handle); /* write hazard lies */ + + gem_close(i915, obj.handle); + syncobj_destroy(i915, fence.handle); + + out = execbuf.rsvd2 >> 32; + igt_assert_eq(sync_fence_status(out), -EDEADLK); + close(out); +} + +static void test_syncobj_future_pair(int i915, unsigned int engine) +{ + struct drm_i915_gem_exec_object2 obj = { + .offset = 24 << 20, + .handle = submitN_batches(i915, 24 << 20, 2), + .flags = EXEC_OBJECT_PINNED, + }; + struct drm_i915_gem_exec_fence fence[2] = { + { .handle = syncobj_create(i915, 0) }, + { .handle = syncobj_create(i915, 0) } + }; + struct drm_i915_gem_execbuffer2 execbuf = { + .buffers_ptr = to_user_pointer(&obj), + .buffer_count = 1, + .cliprects_ptr = to_user_pointer(fence), + .num_cliprects = 2, + }; + const struct intel_execution_engine2 *e; + + __for_each_physical_engine(i915, e) { + int out = 0; + + gem_write(i915, obj.handle, 0, &out, sizeof(out)); + fence[0].handle = syncobj_create(i915, 0); + fence[1].handle = syncobj_create(i915, 0); + + fence[0].flags = I915_EXEC_FENCE_SIGNAL; + fence[1].flags = + I915_EXEC_FENCE_WAIT | I915_EXEC_FENCE_WAIT_SUBMIT; + execbuf.batch_start_offset = 1024; + execbuf.flags = + engine | I915_EXEC_FENCE_ARRAY | I915_EXEC_FENCE_OUT; + execbuf.rsvd1 = gem_context_create(i915); + igt_require(__gem_execbuf_wr(i915, &execbuf) == 0); + gem_context_destroy(i915, execbuf.rsvd1); + execbuf.rsvd2 >>= 32; + + fence[0].flags = + I915_EXEC_FENCE_WAIT | I915_EXEC_FENCE_WAIT_SUBMIT; + fence[1].flags = I915_EXEC_FENCE_SIGNAL; + execbuf.batch_start_offset = 2048; + execbuf.flags = + e->flags | I915_EXEC_FENCE_ARRAY | I915_EXEC_FENCE_OUT; + execbuf.rsvd1 = gem_context_create(i915); + gem_execbuf_wr(i915, &execbuf); + gem_context_destroy(i915, execbuf.rsvd1); + + syncobj_destroy(i915, fence[0].handle); + syncobj_destroy(i915, fence[1].handle); + + gem_sync(i915, obj.handle); /* write hazard lies */ + + out = execbuf.rsvd2; + igt_assert_eq(sync_fence_status(out), 1); + close(out); + + out = execbuf.rsvd2 >> 32; + igt_assert_eq(sync_fence_status(out), 1); + close(out); + + gem_read(i915, obj.handle, 0, &out, sizeof(out)); + igt_assert_eq(out, 16); + } + + gem_close(i915, obj.handle); +} + +static void test_syncobj_future_group(int i915, unsigned int engine, int count) +{ + struct drm_i915_gem_exec_object2 obj = { + .offset = 24 << 20, + .handle = submitN_batches(i915, 24 << 20, count), + .flags = EXEC_OBJECT_PINNED, + }; + struct drm_i915_gem_exec_fence fence[count]; + struct drm_i915_gem_execbuffer2 execbuf = { + .buffers_ptr = to_user_pointer(&obj), + .buffer_count = 1, + .cliprects_ptr = to_user_pointer(fence), + .num_cliprects = count, + .flags = engine | I915_EXEC_FENCE_ARRAY | I915_EXEC_FENCE_OUT, + }; + int out[count]; + uint32_t result; + + for (int i = 0; i < count; i++) { + fence[i].handle = syncobj_create(i915, 0); + fence[i].flags = + I915_EXEC_FENCE_WAIT | I915_EXEC_FENCE_WAIT_SUBMIT; + } + + for (int i = 0; i < count; i++) { + fence[i].flags = I915_EXEC_FENCE_SIGNAL; + + execbuf.batch_start_offset = 1024 * (i + 1); + execbuf.rsvd1 = gem_context_create(i915); + gem_execbuf_wr(i915, &execbuf); + gem_context_destroy(i915, execbuf.rsvd1); + + out[i] = execbuf.rsvd2 >> 32; + fence[i].flags = + I915_EXEC_FENCE_WAIT | I915_EXEC_FENCE_WAIT_SUBMIT; + } + gem_sync(i915, obj.handle); /* write hazard lies */ + + /* As both batches were waiting for the other to start -- deadlock? */ + for (int i = 0; i < count; i++) { + syncobj_destroy(i915, fence[i].handle); + igt_assert_eq(sync_fence_status(out[i]), 1); + close(out[i]); + } + + /* Nevertheless, we ignored^Wresolved the deadlock and let them run */ + gem_read(i915, obj.handle, 0, &result, sizeof(result)); + igt_assert_eq(result, 8 * count); + gem_close(i915, obj.handle); +} + + +static void +test_syncobj_future_deadlock(int i915, unsigned int engine, int flags) +{ + struct drm_i915_gem_exec_object2 obj = { + .offset = 24 << 20, + .handle = future_batch(i915, 24 << 20), + .flags = EXEC_OBJECT_PINNED, + }; + struct drm_i915_gem_exec_fence fence = {}; + struct drm_i915_gem_execbuffer2 execbuf = { + .buffers_ptr = to_user_pointer(&obj), + .buffer_count = 1, + .cliprects_ptr = to_user_pointer(&fence), + .num_cliprects = 1, + }; + const struct intel_execution_engine2 *e; + + __for_each_physical_engine(i915, e) { + int out; + + fence.handle = syncobj_create(i915, 0), + + fence.flags = I915_EXEC_FENCE_WAIT | flags; + execbuf.batch_start_offset = 0; + execbuf.flags = engine | I915_EXEC_FENCE_ARRAY, + execbuf.flags |= I915_EXEC_FENCE_OUT; + execbuf.rsvd1 = 0; + gem_execbuf_wr(i915, &execbuf); /* writes 1 */ + + fence.flags = I915_EXEC_FENCE_SIGNAL; + execbuf.batch_start_offset = 64; + execbuf.flags = e->flags | I915_EXEC_FENCE_ARRAY, + execbuf.flags |= I915_EXEC_FENCE_OUT | I915_EXEC_FENCE_IN; + execbuf.rsvd1 = gem_context_clone_with_engines(i915, 0); + execbuf.rsvd2 >>= 32; + gem_execbuf_wr(i915, &execbuf); /* writes 2 */ + gem_context_destroy(i915, execbuf.rsvd1); + + syncobj_destroy(i915, fence.handle); + gem_sync(i915, obj.handle); + + /* How should this deadlock be resolved? */ + out = execbuf.rsvd2; + igt_assert_eq(sync_fence_status(out), -EDEADLK); + close(out); + + out = execbuf.rsvd2 >> 32; + igt_assert_eq(sync_fence_status(out), -EDEADLK); + close(out); + } + + gem_close(i915, obj.handle); +} + +static void +test_syncobj_future_cycle(int i915, unsigned int engine, int flags) +{ + struct drm_i915_gem_exec_object2 obj = { + .offset = 24 << 20, + .handle = future_batch(i915, 24 << 20), + .flags = EXEC_OBJECT_PINNED, + }; + const struct intel_execution_engine2 *e1, *e2; + + __for_each_physical_engine(i915, e1) { + __for_each_physical_engine(i915, e2) { + struct drm_i915_gem_exec_fence fence = { + .handle = syncobj_create(i915, 0), + }; + struct drm_i915_gem_execbuffer2 execbuf = { + .buffers_ptr = to_user_pointer(&obj), + .buffer_count = 1, + .cliprects_ptr = to_user_pointer(&fence), + .num_cliprects = 1, + .flags = engine | I915_EXEC_FENCE_ARRAY, + }; + int out; + + fence.flags = I915_EXEC_FENCE_WAIT | flags; + execbuf.batch_start_offset = 0; + execbuf.flags |= I915_EXEC_FENCE_OUT; + igt_require(__gem_execbuf_wr(i915, &execbuf) == 0); + + fence.flags = 0; + execbuf.rsvd1 = gem_context_clone_with_engines(i915, 0); + execbuf.rsvd2 >>= 32; + execbuf.flags &= ~I915_EXEC_RING_MASK; + execbuf.flags |= e1->flags | I915_EXEC_FENCE_IN; + gem_execbuf_wr(i915, &execbuf); + gem_context_destroy(i915, execbuf.rsvd1); + close(execbuf.rsvd2); + + fence.flags = I915_EXEC_FENCE_SIGNAL; + execbuf.rsvd1 = gem_context_clone_with_engines(i915, 0); + execbuf.rsvd2 >>= 32; + execbuf.flags &= ~I915_EXEC_RING_MASK; + execbuf.flags |= e2->flags; + execbuf.batch_start_offset = 64; + gem_execbuf_wr(i915, &execbuf); /* writes 2 */ + gem_context_destroy(i915, execbuf.rsvd1); + + syncobj_destroy(i915, fence.handle); + gem_sync(i915, obj.handle); + + + /* How should this deadlock be resolved? */ + out = execbuf.rsvd2 >> 32; + igt_assert_eq(sync_fence_status(out), -EDEADLK); + close(out); + + out = execbuf.rsvd2; + igt_assert_eq(sync_fence_status(out), -EDEADLK); + close(out); + }} + + gem_close(i915, obj.handle); +} + static void test_syncobj_export(int fd) { const uint32_t bbe = MI_BATCH_BUFFER_END; @@ -1574,6 +2131,117 @@ static void test_syncobj_channel(int fd) syncobj_destroy(fd, syncobj[i]); } +static bool has_future_syncobj(int i915) +{ + struct drm_i915_gem_exec_object2 obj = { + .handle = batch_create(i915), + }; + struct drm_i915_gem_exec_fence fence = { + .handle = syncobj_create(i915, 0), + .flags = I915_EXEC_FENCE_WAIT | I915_EXEC_FENCE_SIGNAL, + }; + struct drm_i915_gem_execbuffer2 execbuf = { + .buffers_ptr = to_user_pointer(&obj), + .buffer_count = 1, + .cliprects_ptr = to_user_pointer(&fence), + .num_cliprects = 1, + .flags = I915_EXEC_FENCE_ARRAY, + }; + bool result; + + result = __gem_execbuf(i915, &execbuf) == 0; + gem_close(i915, obj.handle); + + return result; +} + +static void syncobj_futures(int i915) +{ + const struct intel_execution_engine2 *e; + + igt_fixture { + igt_require(gem_scheduler_enabled(i915)); + igt_require(has_future_syncobj(i915)); + } + + igt_subtest_with_dynamic("syncobj-future") + __for_each_physical_engine(i915, e) { + igt_dynamic_f("%s", e->name) + test_syncobj_future(i915, e->flags); + } + + igt_subtest_with_dynamic("syncobj-future-past") + __for_each_physical_engine(i915, e) { + igt_dynamic_f("%s", e->name) + test_syncobj_future_past(i915, e->flags, 0); + } + + + igt_subtest_with_dynamic("syncobj-future-submit") + __for_each_physical_engine(i915, e) { + igt_dynamic_f("%s", e->name) + test_syncobj_future_submit(i915, e->flags); + } + + igt_subtest_with_dynamic("syncobj-future-submit-past") + __for_each_physical_engine(i915, e) { + igt_dynamic_f("%s", e->name) + test_syncobj_future_past(i915, e->flags, + I915_EXEC_FENCE_WAIT_SUBMIT); + } + + igt_subtest_with_dynamic("syncobj-future-self") + __for_each_physical_engine(i915, e) { + igt_dynamic_f("%s", e->name) + test_syncobj_future_self(i915, e->flags, 0); + } + + igt_subtest_with_dynamic("syncobj-future-self-submit") + __for_each_physical_engine(i915, e) { + igt_dynamic_f("%s", e->name) + test_syncobj_future_self(i915, e->flags, + I915_EXEC_FENCE_WAIT_SUBMIT); + } + + igt_subtest_with_dynamic("syncobj-future-pair") + __for_each_physical_engine(i915, e) { + igt_dynamic_f("%s", e->name) + test_syncobj_future_pair(i915, e->flags); + } + + igt_subtest_with_dynamic("syncobj-future-group") + __for_each_physical_engine(i915, e) { + igt_dynamic_f("%s", e->name) + test_syncobj_future_group(i915, e->flags, 67); + } + + igt_subtest_with_dynamic("syncobj-future-deadlock") + __for_each_physical_engine(i915, e) { + igt_dynamic_f("%s", e->name) + test_syncobj_future_deadlock(i915, e->flags, 0); + } + + igt_subtest_with_dynamic("syncobj-future-submit-deadlock") + __for_each_physical_engine(i915, e) { + igt_dynamic_f("%s", e->name) + test_syncobj_future_deadlock(i915, e->flags, + I915_EXEC_FENCE_WAIT_SUBMIT); + } + + igt_subtest_with_dynamic("syncobj-future-cycle") + __for_each_physical_engine(i915, e) { + igt_dynamic_f("%s", e->name) + test_syncobj_future_cycle(i915, e->flags, 0); + } + + igt_subtest_with_dynamic("syncobj-future-submit-cycle") + __for_each_physical_engine(i915, e) { + igt_dynamic_f("%s", e->name) + test_syncobj_future_cycle(i915, e->flags, + I915_EXEC_FENCE_WAIT_SUBMIT); + } +} + igt_main { const struct intel_execution_engine2 *e; @@ -1786,6 +2454,9 @@ igt_main igt_subtest("syncobj-invalid-wait") test_syncobj_invalid_wait(i915); + igt_subtest("syncobj-incomplete-wait-submit") + test_syncobj_incomplete_wait_submit(i915); + igt_subtest("syncobj-invalid-flags") test_syncobj_invalid_flags(i915); @@ -1795,6 +2466,9 @@ igt_main igt_subtest("syncobj-wait") test_syncobj_wait(i915); + igt_subtest_group + syncobj_futures(i915); + igt_subtest("syncobj-export") test_syncobj_export(i915); -- 2.26.2 _______________________________________________ igt-dev mailing list igt-dev@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/igt-dev
next prev parent reply other threads:[~2020-05-13 17:02 UTC|newest] Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top 2020-05-13 17:02 [Intel-gfx] [PATCH i-g-t 1/2] lib/i915: Report scheduler caps for timeslicing Chris Wilson 2020-05-13 17:02 ` [igt-dev] " Chris Wilson 2020-05-13 17:02 ` Chris Wilson [this message] 2020-05-13 17:02 ` [igt-dev] [PATCH i-g-t 2/2] i915/gem_exec_fence: Teach invalid-wait about invalid future fences Chris Wilson 2020-05-13 18:10 ` [igt-dev] ✓ Fi.CI.BAT: success for series starting with [i-g-t,1/2] lib/i915: Report scheduler caps for timeslicing Patchwork 2020-05-13 21:47 ` [igt-dev] ✓ Fi.CI.IGT: " Patchwork 2020-05-14 3:41 ` [Intel-gfx] [igt-dev] [PATCH i-g-t 1/2] " Petri Latvala 2020-05-14 3:41 ` Petri Latvala 2020-05-15 11:44 ` [igt-dev] ✗ GitLab.Pipeline: failure for series starting with [i-g-t,1/2] " Patchwork -- strict thread matches above, loose matches on Subject: below -- 2020-05-05 22:09 [Intel-gfx] [PATCH i-g-t 1/2] " Chris Wilson 2020-05-05 22:09 ` [Intel-gfx] [PATCH i-g-t 2/2] i915/gem_exec_fence: Teach invalid-wait about invalid future fences Chris Wilson 2020-05-05 13:38 [Intel-gfx] [PATCH i-g-t 1/2] lib/i915: Report scheduler caps for timeslicing Chris Wilson 2020-05-05 13:38 ` [Intel-gfx] [PATCH i-g-t 2/2] i915/gem_exec_fence: Teach invalid-wait about invalid future fences Chris Wilson
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=20200513170224.848726-2-chris@chris-wilson.co.uk \ --to=chris@chris-wilson.co.uk \ --cc=igt-dev@lists.freedesktop.org \ --cc=intel-gfx@lists.freedesktop.org \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: linkBe sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.