All of lore.kernel.org
 help / color / mirror / Atom feed
* [Intel-gfx] [PATCH i-g-t 1/2] lib/i915: Report scheduler caps for timeslicing
@ 2020-05-05 22:09 ` Chris Wilson
  0 siblings, 0 replies; 8+ messages in thread
From: Chris Wilson @ 2020-05-05 22:09 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev, Chris Wilson

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 include/drm-uapi/i915_drm.h |  8 +++++---
 lib/i915/gem_scheduler.c    | 15 +++++++++++++++
 lib/i915/gem_scheduler.h    |  1 +
 3 files changed, 21 insertions(+), 3 deletions(-)

diff --git a/include/drm-uapi/i915_drm.h b/include/drm-uapi/i915_drm.h
index 2b55af13a..a222b6bfb 100644
--- a/include/drm-uapi/i915_drm.h
+++ b/include/drm-uapi/i915_drm.h
@@ -523,6 +523,7 @@ typedef struct drm_i915_irq_wait {
 #define   I915_SCHEDULER_CAP_PREEMPTION	(1ul << 2)
 #define   I915_SCHEDULER_CAP_SEMAPHORES	(1ul << 3)
 #define   I915_SCHEDULER_CAP_ENGINE_BUSY_STATS	(1ul << 4)
+#define   I915_SCHEDULER_CAP_TIMESLICING	(1ul << 5)
 
 #define I915_PARAM_HUC_STATUS		 42
 
@@ -1040,9 +1041,10 @@ struct drm_i915_gem_exec_fence {
 	 */
 	__u32 handle;
 
-#define I915_EXEC_FENCE_WAIT            (1<<0)
-#define I915_EXEC_FENCE_SIGNAL          (1<<1)
-#define __I915_EXEC_FENCE_UNKNOWN_FLAGS (-(I915_EXEC_FENCE_SIGNAL << 1))
+#define I915_EXEC_FENCE_WAIT            (1u << 0)
+#define I915_EXEC_FENCE_SIGNAL          (1u << 1)
+#define I915_EXEC_FENCE_WAIT_SUBMIT     (1u << 2)
+#define __I915_EXEC_FENCE_UNKNOWN_FLAGS (-(I915_EXEC_FENCE_WAIT_SUBMIT << 1))
 	__u32 flags;
 };
 
diff --git a/lib/i915/gem_scheduler.c b/lib/i915/gem_scheduler.c
index 1beb85dec..a1dc694e5 100644
--- a/lib/i915/gem_scheduler.c
+++ b/lib/i915/gem_scheduler.c
@@ -131,6 +131,19 @@ bool gem_scheduler_has_engine_busy_stats(int fd)
 		I915_SCHEDULER_CAP_ENGINE_BUSY_STATS;
 }
 
+/**
+ * gem_scheduler_has_timeslicing:
+ * @fd: open i915 drm file descriptor
+ *
+ * Feature test macro to query whether the driver supports using HW preemption
+ * to implement timeslicing of userspace batches. This allows userspace to
+ * implement micro-level scheduling within their own batches.
+ */
+bool gem_scheduler_has_timeslicing(int fd)
+{
+	return gem_scheduler_capability(fd) & I915_SCHEDULER_CAP_TIMESLICING;
+}
+
 /**
  * gem_scheduler_print_capability:
  * @fd: open i915 drm file descriptor
@@ -151,6 +164,8 @@ void gem_scheduler_print_capability(int fd)
 		igt_info(" - With preemption enabled\n");
 	if (caps & I915_SCHEDULER_CAP_SEMAPHORES)
 		igt_info(" - With HW semaphores enabled\n");
+	if (caps & I915_SCHEDULER_CAP_TIMESLICING)
+		igt_info(" - With user timeslicing enabled\n");
 	if (caps & I915_SCHEDULER_CAP_ENGINE_BUSY_STATS)
 		igt_info(" - With engine busy statistics\n");
 }
diff --git a/lib/i915/gem_scheduler.h b/lib/i915/gem_scheduler.h
index 14bd4cac4..d43e84bd2 100644
--- a/lib/i915/gem_scheduler.h
+++ b/lib/i915/gem_scheduler.h
@@ -32,6 +32,7 @@ bool gem_scheduler_has_ctx_priority(int fd);
 bool gem_scheduler_has_preemption(int fd);
 bool gem_scheduler_has_semaphores(int fd);
 bool gem_scheduler_has_engine_busy_stats(int fd);
+bool gem_scheduler_has_timeslicing(int fd);
 void gem_scheduler_print_capability(int fd);
 
 #endif /* GEM_SCHEDULER_H */
-- 
2.26.2

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

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [igt-dev] [PATCH i-g-t 1/2] lib/i915: Report scheduler caps for timeslicing
@ 2020-05-05 22:09 ` Chris Wilson
  0 siblings, 0 replies; 8+ messages in thread
From: Chris Wilson @ 2020-05-05 22:09 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev, Chris Wilson

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 include/drm-uapi/i915_drm.h |  8 +++++---
 lib/i915/gem_scheduler.c    | 15 +++++++++++++++
 lib/i915/gem_scheduler.h    |  1 +
 3 files changed, 21 insertions(+), 3 deletions(-)

diff --git a/include/drm-uapi/i915_drm.h b/include/drm-uapi/i915_drm.h
index 2b55af13a..a222b6bfb 100644
--- a/include/drm-uapi/i915_drm.h
+++ b/include/drm-uapi/i915_drm.h
@@ -523,6 +523,7 @@ typedef struct drm_i915_irq_wait {
 #define   I915_SCHEDULER_CAP_PREEMPTION	(1ul << 2)
 #define   I915_SCHEDULER_CAP_SEMAPHORES	(1ul << 3)
 #define   I915_SCHEDULER_CAP_ENGINE_BUSY_STATS	(1ul << 4)
+#define   I915_SCHEDULER_CAP_TIMESLICING	(1ul << 5)
 
 #define I915_PARAM_HUC_STATUS		 42
 
@@ -1040,9 +1041,10 @@ struct drm_i915_gem_exec_fence {
 	 */
 	__u32 handle;
 
-#define I915_EXEC_FENCE_WAIT            (1<<0)
-#define I915_EXEC_FENCE_SIGNAL          (1<<1)
-#define __I915_EXEC_FENCE_UNKNOWN_FLAGS (-(I915_EXEC_FENCE_SIGNAL << 1))
+#define I915_EXEC_FENCE_WAIT            (1u << 0)
+#define I915_EXEC_FENCE_SIGNAL          (1u << 1)
+#define I915_EXEC_FENCE_WAIT_SUBMIT     (1u << 2)
+#define __I915_EXEC_FENCE_UNKNOWN_FLAGS (-(I915_EXEC_FENCE_WAIT_SUBMIT << 1))
 	__u32 flags;
 };
 
diff --git a/lib/i915/gem_scheduler.c b/lib/i915/gem_scheduler.c
index 1beb85dec..a1dc694e5 100644
--- a/lib/i915/gem_scheduler.c
+++ b/lib/i915/gem_scheduler.c
@@ -131,6 +131,19 @@ bool gem_scheduler_has_engine_busy_stats(int fd)
 		I915_SCHEDULER_CAP_ENGINE_BUSY_STATS;
 }
 
+/**
+ * gem_scheduler_has_timeslicing:
+ * @fd: open i915 drm file descriptor
+ *
+ * Feature test macro to query whether the driver supports using HW preemption
+ * to implement timeslicing of userspace batches. This allows userspace to
+ * implement micro-level scheduling within their own batches.
+ */
+bool gem_scheduler_has_timeslicing(int fd)
+{
+	return gem_scheduler_capability(fd) & I915_SCHEDULER_CAP_TIMESLICING;
+}
+
 /**
  * gem_scheduler_print_capability:
  * @fd: open i915 drm file descriptor
@@ -151,6 +164,8 @@ void gem_scheduler_print_capability(int fd)
 		igt_info(" - With preemption enabled\n");
 	if (caps & I915_SCHEDULER_CAP_SEMAPHORES)
 		igt_info(" - With HW semaphores enabled\n");
+	if (caps & I915_SCHEDULER_CAP_TIMESLICING)
+		igt_info(" - With user timeslicing enabled\n");
 	if (caps & I915_SCHEDULER_CAP_ENGINE_BUSY_STATS)
 		igt_info(" - With engine busy statistics\n");
 }
diff --git a/lib/i915/gem_scheduler.h b/lib/i915/gem_scheduler.h
index 14bd4cac4..d43e84bd2 100644
--- a/lib/i915/gem_scheduler.h
+++ b/lib/i915/gem_scheduler.h
@@ -32,6 +32,7 @@ bool gem_scheduler_has_ctx_priority(int fd);
 bool gem_scheduler_has_preemption(int fd);
 bool gem_scheduler_has_semaphores(int fd);
 bool gem_scheduler_has_engine_busy_stats(int fd);
+bool gem_scheduler_has_timeslicing(int fd);
 void gem_scheduler_print_capability(int fd);
 
 #endif /* GEM_SCHEDULER_H */
-- 
2.26.2

_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [Intel-gfx] [PATCH i-g-t 2/2] i915/gem_exec_fence: Teach invalid-wait about invalid future fences
  2020-05-05 22:09 ` [igt-dev] " Chris Wilson
@ 2020-05-05 22:09   ` Chris Wilson
  -1 siblings, 0 replies; 8+ messages in thread
From: Chris Wilson @ 2020-05-05 22:09 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev, Chris Wilson

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 | 558 +++++++++++++++++++++++++++++++++++-
 1 file changed, 555 insertions(+), 3 deletions(-)

diff --git a/tests/i915/gem_exec_fence.c b/tests/i915/gem_exec_fence.c
index 4b0d87e4d..e51b7452e 100644
--- a/tests/i915/gem_exec_fence.c
+++ b/tests/i915/gem_exec_fence.c
@@ -46,6 +46,15 @@ struct sync_merge_data {
 #define SYNC_IOC_MERGE _IOWR(SYNC_IOC_MAGIC, 3, struct sync_merge_data)
 #endif
 
+#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)
+
 static void store(int fd, const struct intel_execution_engine2 *e,
 		  int fence, uint32_t target, unsigned offset_value)
 {
@@ -907,11 +916,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;
 
@@ -919,14 +929,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;
@@ -1073,6 +1128,398 @@ 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_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;
@@ -1358,6 +1805,105 @@ 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-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;
@@ -1537,6 +2083,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);
 
@@ -1546,6 +2095,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

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [igt-dev] [PATCH i-g-t 2/2] i915/gem_exec_fence: Teach invalid-wait about invalid future fences
@ 2020-05-05 22:09   ` Chris Wilson
  0 siblings, 0 replies; 8+ messages in thread
From: Chris Wilson @ 2020-05-05 22:09 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev, Chris Wilson

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 | 558 +++++++++++++++++++++++++++++++++++-
 1 file changed, 555 insertions(+), 3 deletions(-)

diff --git a/tests/i915/gem_exec_fence.c b/tests/i915/gem_exec_fence.c
index 4b0d87e4d..e51b7452e 100644
--- a/tests/i915/gem_exec_fence.c
+++ b/tests/i915/gem_exec_fence.c
@@ -46,6 +46,15 @@ struct sync_merge_data {
 #define SYNC_IOC_MERGE _IOWR(SYNC_IOC_MAGIC, 3, struct sync_merge_data)
 #endif
 
+#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)
+
 static void store(int fd, const struct intel_execution_engine2 *e,
 		  int fence, uint32_t target, unsigned offset_value)
 {
@@ -907,11 +916,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;
 
@@ -919,14 +929,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;
@@ -1073,6 +1128,398 @@ 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_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;
@@ -1358,6 +1805,105 @@ 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-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;
@@ -1537,6 +2083,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);
 
@@ -1546,6 +2095,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

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [igt-dev] ✓ Fi.CI.BAT: success for series starting with [i-g-t,1/2] lib/i915: Report scheduler caps for timeslicing
  2020-05-05 22:09 ` [igt-dev] " Chris Wilson
  (?)
  (?)
@ 2020-05-05 23:27 ` Patchwork
  -1 siblings, 0 replies; 8+ messages in thread
From: Patchwork @ 2020-05-05 23:27 UTC (permalink / raw)
  To: Chris Wilson; +Cc: igt-dev

== Series Details ==

Series: series starting with [i-g-t,1/2] lib/i915: Report scheduler caps for timeslicing
URL   : https://patchwork.freedesktop.org/series/76974/
State : success

== Summary ==

CI Bug Log - changes from IGT_5634 -> IGTPW_4536
====================================================

Summary
-------

  **SUCCESS**

  No regressions found.

  External URL: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_4536/index.html


Changes
-------

  No changes found


Participating hosts (51 -> 44)
------------------------------

  Missing    (7): fi-ilk-m540 fi-hsw-4200u fi-byt-squawks fi-bsw-cyan fi-ctg-p8600 fi-byt-clapper fi-bdw-samus 


Build changes
-------------

  * CI: CI-20190529 -> None
  * IGT: IGT_5634 -> IGTPW_4536

  CI-20190529: 20190529
  CI_DRM_8433: db68fed086f2ddcdc30e0d9ca5faaba5e55d0d01 @ git://anongit.freedesktop.org/gfx-ci/linux
  IGTPW_4536: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_4536/index.html
  IGT_5634: 3cff0b137bcb2f6b329a6cb0fc03962d8cc65117 @ git://anongit.freedesktop.org/xorg/app/intel-gpu-tools



== Testlist changes ==

+igt@gem_exec_fence@syncobj-future
+igt@gem_exec_fence@syncobj-future-cycle
+igt@gem_exec_fence@syncobj-future-deadlock
+igt@gem_exec_fence@syncobj-future-past
+igt@gem_exec_fence@syncobj-future-self
+igt@gem_exec_fence@syncobj-future-self-submit
+igt@gem_exec_fence@syncobj-future-submit
+igt@gem_exec_fence@syncobj-future-submit-cycle
+igt@gem_exec_fence@syncobj-future-submit-deadlock
+igt@gem_exec_fence@syncobj-future-submit-past
+igt@gem_exec_fence@syncobj-incomplete-wait-submit

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_4536/index.html
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [igt-dev] ✗ Fi.CI.IGT: failure for series starting with [i-g-t,1/2] lib/i915: Report scheduler caps for timeslicing
  2020-05-05 22:09 ` [igt-dev] " Chris Wilson
                   ` (2 preceding siblings ...)
  (?)
@ 2020-05-06  9:35 ` Patchwork
  -1 siblings, 0 replies; 8+ messages in thread
From: Patchwork @ 2020-05-06  9:35 UTC (permalink / raw)
  To: Chris Wilson; +Cc: igt-dev

== Series Details ==

Series: series starting with [i-g-t,1/2] lib/i915: Report scheduler caps for timeslicing
URL   : https://patchwork.freedesktop.org/series/76974/
State : failure

== Summary ==

CI Bug Log - changes from IGT_5634_full -> IGTPW_4536_full
====================================================

Summary
-------

  **FAILURE**

  Serious unknown changes coming with IGTPW_4536_full absolutely need to be
  verified manually.
  
  If you think the reported changes have nothing to do with the changes
  introduced in IGTPW_4536_full, please notify your bug team to allow them
  to document this new failure mode, which will reduce false positives in CI.

  External URL: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_4536/index.html

Possible new issues
-------------------

  Here are the unknown changes that may have been introduced in IGTPW_4536_full:

### IGT changes ###

#### Possible regressions ####

  * igt@gem_ctx_persistence@legacy-engines-hostile@vebox:
    - shard-iclb:         [PASS][1] -> [FAIL][2]
   [1]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_5634/shard-iclb4/igt@gem_ctx_persistence@legacy-engines-hostile@vebox.html
   [2]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_4536/shard-iclb6/igt@gem_ctx_persistence@legacy-engines-hostile@vebox.html

  * {igt@gem_exec_fence@syncobj-future} (NEW):
    - shard-iclb:         NOTRUN -> [SKIP][3] +9 similar issues
   [3]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_4536/shard-iclb1/igt@gem_exec_fence@syncobj-future.html

  * {igt@gem_exec_fence@syncobj-future-submit-cycle} (NEW):
    - shard-tglb:         NOTRUN -> [SKIP][4] +9 similar issues
   [4]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_4536/shard-tglb3/igt@gem_exec_fence@syncobj-future-submit-cycle.html

  
#### Suppressed ####

  The following results come from untrusted machines, tests, or statuses.
  They do not affect the overall result.

  * {igt@i915_selftest@perf@request}:
    - shard-tglb:         [PASS][5] -> [DMESG-FAIL][6]
   [5]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_5634/shard-tglb6/igt@i915_selftest@perf@request.html
   [6]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_4536/shard-tglb1/igt@i915_selftest@perf@request.html

  
New tests
---------

  New tests have been introduced between IGT_5634_full and IGTPW_4536_full:

### New IGT tests (11) ###

  * igt@gem_exec_fence@syncobj-future:
    - Statuses : 7 skip(s)
    - Exec time: [0.0] s

  * igt@gem_exec_fence@syncobj-future-cycle:
    - Statuses : 7 skip(s)
    - Exec time: [0.0] s

  * igt@gem_exec_fence@syncobj-future-deadlock:
    - Statuses : 6 skip(s)
    - Exec time: [0.0] s

  * igt@gem_exec_fence@syncobj-future-past:
    - Statuses : 7 skip(s)
    - Exec time: [0.0] s

  * igt@gem_exec_fence@syncobj-future-self:
    - Statuses : 7 skip(s)
    - Exec time: [0.0] s

  * igt@gem_exec_fence@syncobj-future-self-submit:
    - Statuses : 7 skip(s)
    - Exec time: [0.0] s

  * igt@gem_exec_fence@syncobj-future-submit:
    - Statuses : 6 skip(s)
    - Exec time: [0.0] s

  * igt@gem_exec_fence@syncobj-future-submit-cycle:
    - Statuses : 7 skip(s)
    - Exec time: [0.0] s

  * igt@gem_exec_fence@syncobj-future-submit-deadlock:
    - Statuses : 7 skip(s)
    - Exec time: [0.0] s

  * igt@gem_exec_fence@syncobj-future-submit-past:
    - Statuses : 7 skip(s)
    - Exec time: [0.0] s

  * igt@gem_exec_fence@syncobj-incomplete-wait-submit:
    - Statuses : 7 pass(s)
    - Exec time: [0.0, 0.00] s

  

Known issues
------------

  Here are the changes found in IGTPW_4536_full that come from known issues:

### IGT changes ###

#### Issues hit ####

  * igt@gen9_exec_parse@allowed-all:
    - shard-kbl:          [PASS][7] -> [DMESG-WARN][8] ([i915#1436] / [i915#716])
   [7]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_5634/shard-kbl2/igt@gen9_exec_parse@allowed-all.html
   [8]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_4536/shard-kbl6/igt@gen9_exec_parse@allowed-all.html

  * igt@kms_cursor_crc@pipe-a-cursor-256x256-offscreen:
    - shard-kbl:          [PASS][9] -> [FAIL][10] ([i915#54] / [i915#93] / [i915#95])
   [9]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_5634/shard-kbl6/igt@kms_cursor_crc@pipe-a-cursor-256x256-offscreen.html
   [10]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_4536/shard-kbl3/igt@kms_cursor_crc@pipe-a-cursor-256x256-offscreen.html

  * igt@kms_cursor_crc@pipe-c-cursor-suspend:
    - shard-kbl:          [PASS][11] -> [DMESG-WARN][12] ([i915#180])
   [11]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_5634/shard-kbl2/igt@kms_cursor_crc@pipe-c-cursor-suspend.html
   [12]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_4536/shard-kbl1/igt@kms_cursor_crc@pipe-c-cursor-suspend.html

  * igt@kms_hdr@bpc-switch-suspend:
    - shard-kbl:          [PASS][13] -> [INCOMPLETE][14] ([i915#155])
   [13]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_5634/shard-kbl7/igt@kms_hdr@bpc-switch-suspend.html
   [14]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_4536/shard-kbl2/igt@kms_hdr@bpc-switch-suspend.html

  * igt@kms_plane_alpha_blend@pipe-a-constant-alpha-min:
    - shard-kbl:          [PASS][15] -> [FAIL][16] ([fdo#108145] / [i915#265] / [i915#93] / [i915#95])
   [15]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_5634/shard-kbl6/igt@kms_plane_alpha_blend@pipe-a-constant-alpha-min.html
   [16]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_4536/shard-kbl7/igt@kms_plane_alpha_blend@pipe-a-constant-alpha-min.html
    - shard-apl:          [PASS][17] -> [FAIL][18] ([fdo#108145] / [i915#265] / [i915#95])
   [17]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_5634/shard-apl4/igt@kms_plane_alpha_blend@pipe-a-constant-alpha-min.html
   [18]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_4536/shard-apl7/igt@kms_plane_alpha_blend@pipe-a-constant-alpha-min.html

  * igt@kms_psr@psr2_sprite_render:
    - shard-iclb:         [PASS][19] -> [SKIP][20] ([fdo#109441])
   [19]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_5634/shard-iclb2/igt@kms_psr@psr2_sprite_render.html
   [20]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_4536/shard-iclb8/igt@kms_psr@psr2_sprite_render.html

  * igt@kms_setmode@basic:
    - shard-kbl:          [PASS][21] -> [FAIL][22] ([i915#31])
   [21]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_5634/shard-kbl1/igt@kms_setmode@basic.html
   [22]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_4536/shard-kbl1/igt@kms_setmode@basic.html

  
#### Possible fixes ####

  * igt@gem_exec_params@invalid-bsd-ring:
    - shard-iclb:         [SKIP][23] ([fdo#109276]) -> [PASS][24]
   [23]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_5634/shard-iclb7/igt@gem_exec_params@invalid-bsd-ring.html
   [24]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_4536/shard-iclb2/igt@gem_exec_params@invalid-bsd-ring.html

  * igt@i915_pm_dc@dc6-psr:
    - shard-iclb:         [FAIL][25] ([i915#454]) -> [PASS][26]
   [25]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_5634/shard-iclb2/igt@i915_pm_dc@dc6-psr.html
   [26]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_4536/shard-iclb1/igt@i915_pm_dc@dc6-psr.html

  * igt@kms_cursor_crc@pipe-a-cursor-64x21-offscreen:
    - shard-kbl:          [FAIL][27] ([i915#54] / [i915#93] / [i915#95]) -> [PASS][28] +1 similar issue
   [27]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_5634/shard-kbl3/igt@kms_cursor_crc@pipe-a-cursor-64x21-offscreen.html
   [28]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_4536/shard-kbl7/igt@kms_cursor_crc@pipe-a-cursor-64x21-offscreen.html

  * igt@kms_cursor_crc@pipe-b-cursor-suspend:
    - shard-kbl:          [DMESG-WARN][29] ([i915#180]) -> [PASS][30]
   [29]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_5634/shard-kbl1/igt@kms_cursor_crc@pipe-b-cursor-suspend.html
   [30]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_4536/shard-kbl1/igt@kms_cursor_crc@pipe-b-cursor-suspend.html

  * igt@kms_cursor_crc@pipe-c-cursor-dpms:
    - shard-apl:          [FAIL][31] ([i915#54]) -> [PASS][32]
   [31]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_5634/shard-apl6/igt@kms_cursor_crc@pipe-c-cursor-dpms.html
   [32]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_4536/shard-apl3/igt@kms_cursor_crc@pipe-c-cursor-dpms.html
    - shard-kbl:          [FAIL][33] ([i915#54]) -> [PASS][34]
   [33]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_5634/shard-kbl4/igt@kms_cursor_crc@pipe-c-cursor-dpms.html
   [34]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_4536/shard-kbl6/igt@kms_cursor_crc@pipe-c-cursor-dpms.html

  * igt@kms_cursor_legacy@all-pipes-torture-move:
    - shard-hsw:          [DMESG-WARN][35] ([i915#128]) -> [PASS][36]
   [35]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_5634/shard-hsw4/igt@kms_cursor_legacy@all-pipes-torture-move.html
   [36]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_4536/shard-hsw6/igt@kms_cursor_legacy@all-pipes-torture-move.html

  * {igt@kms_flip@flip-vs-suspend-interruptible@c-dp1}:
    - shard-apl:          [DMESG-WARN][37] ([i915#180]) -> [PASS][38] +3 similar issues
   [37]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_5634/shard-apl8/igt@kms_flip@flip-vs-suspend-interruptible@c-dp1.html
   [38]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_4536/shard-apl2/igt@kms_flip@flip-vs-suspend-interruptible@c-dp1.html

  * igt@kms_pipe_crc_basic@suspend-read-crc-pipe-b:
    - shard-kbl:          [INCOMPLETE][39] ([i915#155]) -> [PASS][40]
   [39]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_5634/shard-kbl2/igt@kms_pipe_crc_basic@suspend-read-crc-pipe-b.html
   [40]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_4536/shard-kbl7/igt@kms_pipe_crc_basic@suspend-read-crc-pipe-b.html

  * igt@kms_psr@psr2_sprite_mmap_cpu:
    - shard-iclb:         [SKIP][41] ([fdo#109441]) -> [PASS][42]
   [41]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_5634/shard-iclb1/igt@kms_psr@psr2_sprite_mmap_cpu.html
   [42]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_4536/shard-iclb2/igt@kms_psr@psr2_sprite_mmap_cpu.html

  
#### Warnings ####

  * igt@i915_pm_dc@dc6-psr:
    - shard-tglb:         [SKIP][43] ([i915#468]) -> [FAIL][44] ([i915#454]) +1 similar issue
   [43]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_5634/shard-tglb2/igt@i915_pm_dc@dc6-psr.html
   [44]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_4536/shard-tglb7/igt@i915_pm_dc@dc6-psr.html

  * igt@kms_content_protection@atomic-dpms:
    - shard-apl:          [TIMEOUT][45] ([i915#1319]) -> [FAIL][46] ([fdo#110321] / [fdo#110336])
   [45]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_5634/shard-apl7/igt@kms_content_protection@atomic-dpms.html
   [46]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_4536/shard-apl7/igt@kms_content_protection@atomic-dpms.html

  * igt@kms_fbcon_fbt@fbc-suspend:
    - shard-apl:          [FAIL][47] ([i915#1525]) -> [FAIL][48] ([i915#95])
   [47]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_5634/shard-apl4/igt@kms_fbcon_fbt@fbc-suspend.html
   [48]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_4536/shard-apl2/igt@kms_fbcon_fbt@fbc-suspend.html
    - shard-kbl:          [FAIL][49] ([i915#64]) -> [FAIL][50] ([i915#93] / [i915#95])
   [49]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_5634/shard-kbl6/igt@kms_fbcon_fbt@fbc-suspend.html
   [50]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_4536/shard-kbl4/igt@kms_fbcon_fbt@fbc-suspend.html

  
  {name}: This element is suppressed. This means it is ignored when computing
          the status of the difference (SUCCESS, WARNING, or FAILURE).

  [fdo#108145]: https://bugs.freedesktop.org/show_bug.cgi?id=108145
  [fdo#109276]: https://bugs.freedesktop.org/show_bug.cgi?id=109276
  [fdo#109441]: https://bugs.freedesktop.org/show_bug.cgi?id=109441
  [fdo#110321]: https://bugs.freedesktop.org/show_bug.cgi?id=110321
  [fdo#110336]: https://bugs.freedesktop.org/show_bug.cgi?id=110336
  [i915#128]: https://gitlab.freedesktop.org/drm/intel/issues/128
  [i915#1319]: https://gitlab.freedesktop.org/drm/intel/issues/1319
  [i915#1436]: https://gitlab.freedesktop.org/drm/intel/issues/1436
  [i915#1525]: https://gitlab.freedesktop.org/drm/intel/issues/1525
  [i915#1542]: https://gitlab.freedesktop.org/drm/intel/issues/1542
  [i915#155]: https://gitlab.freedesktop.org/drm/intel/issues/155
  [i915#180]: https://gitlab.freedesktop.org/drm/intel/issues/180
  [i915#265]: https://gitlab.freedesktop.org/drm/intel/issues/265
  [i915#31]: https://gitlab.freedesktop.org/drm/intel/issues/31
  [i915#454]: https://gitlab.freedesktop.org/drm/intel/issues/454
  [i915#468]: https://gitlab.freedesktop.org/drm/intel/issues/468
  [i915#54]: https://gitlab.freedesktop.org/drm/intel/issues/54
  [i915#64]: https://gitlab.freedesktop.org/drm/intel/issues/64
  [i915#716]: https://gitlab.freedesktop.org/drm/intel/issues/716
  [i915#93]: https://gitlab.freedesktop.org/drm/intel/issues/93
  [i915#95]: https://gitlab.freedesktop.org/drm/intel/issues/95


Participating hosts (8 -> 8)
------------------------------

  No changes in participating hosts


Build changes
-------------

  * CI: CI-20190529 -> None
  * IGT: IGT_5634 -> IGTPW_4536

  CI-20190529: 20190529
  CI_DRM_8433: db68fed086f2ddcdc30e0d9ca5faaba5e55d0d01 @ git://anongit.freedesktop.org/gfx-ci/linux
  IGTPW_4536: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_4536/index.html
  IGT_5634: 3cff0b137bcb2f6b329a6cb0fc03962d8cc65117 @ git://anongit.freedesktop.org/xorg/app/intel-gpu-tools

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_4536/index.html
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [Intel-gfx] [PATCH i-g-t 2/2] i915/gem_exec_fence: Teach invalid-wait about invalid future fences
  2020-05-13 17:02 [Intel-gfx] [PATCH i-g-t 1/2] " Chris Wilson
@ 2020-05-13 17:02 ` Chris Wilson
  0 siblings, 0 replies; 8+ messages in thread
From: Chris Wilson @ 2020-05-13 17:02 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev, Chris Wilson

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

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [Intel-gfx] [PATCH i-g-t 2/2] i915/gem_exec_fence: Teach invalid-wait about invalid future fences
  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 ` Chris Wilson
  0 siblings, 0 replies; 8+ messages in thread
From: Chris Wilson @ 2020-05-05 13:38 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev, Chris Wilson

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 | 430 +++++++++++++++++++++++++++++++++++-
 1 file changed, 427 insertions(+), 3 deletions(-)

diff --git a/tests/i915/gem_exec_fence.c b/tests/i915/gem_exec_fence.c
index 17fdaebd5..374b273e4 100644
--- a/tests/i915/gem_exec_fence.c
+++ b/tests/i915/gem_exec_fence.c
@@ -47,6 +47,15 @@ struct sync_merge_data {
 #define SYNC_IOC_MERGE _IOWR(SYNC_IOC_MAGIC, 3, struct sync_merge_data)
 #endif
 
+#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)
+
 static void store(int fd, const struct intel_execution_engine2 *e,
 		  int fence, uint32_t target, unsigned offset_value)
 {
@@ -913,11 +922,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;
 
@@ -925,14 +935,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;
@@ -1079,6 +1134,319 @@ 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 = {
+		.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;
+
+	/*
+	 * 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));
+
+	execbuf.rsvd1 = gem_context_create(i915);
+	fence.flags = I915_EXEC_FENCE_WAIT;
+	execbuf.batch_start_offset = 0;
+	igt_require(__gem_execbuf(i915, &execbuf) == 0); /* writes 1 */
+	gem_context_destroy(i915, execbuf.rsvd1);
+
+	execbuf.rsvd1 = gem_context_create(i915);
+	fence.flags = I915_EXEC_FENCE_SIGNAL;
+	execbuf.batch_start_offset = 64;
+	gem_execbuf(i915, &execbuf); /* writes 2 */
+	gem_context_destroy(i915, execbuf.rsvd1);
+
+	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);
+	syncobj_destroy(i915, fence.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,
+	};
+	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;
+
+	/*
+	 * 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));
+
+	execbuf.rsvd1 = gem_context_create(i915);
+	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_create(i915);
+	fence.flags = I915_EXEC_FENCE_SIGNAL;
+	execbuf.batch_start_offset = 64;
+	gem_execbuf(i915, &execbuf); /* writes 2 */
+	gem_context_destroy(i915, execbuf.rsvd1);
+
+	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);
+	syncobj_destroy(i915, fence.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,
+	};
+	uint32_t result;
+	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_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);
+	syncobj_destroy(i915, fence.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 = {
+		.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 */
+
+	fence.flags = I915_EXEC_FENCE_SIGNAL;
+	execbuf.rsvd1 = gem_context_create(i915);
+	execbuf.rsvd2 >>= 32;
+	execbuf.flags |= I915_EXEC_FENCE_IN;
+	execbuf.batch_start_offset = 64;
+	gem_execbuf_wr(i915, &execbuf); /* writes 2 */
+	gem_context_destroy(i915, execbuf.rsvd1);
+
+	gem_sync(i915, obj.handle);
+
+	gem_close(i915, obj.handle);
+	syncobj_destroy(i915, fence.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);
+}
+
 static void test_syncobj_export(int fd)
 {
 	const uint32_t bbe = MI_BATCH_BUFFER_END;
@@ -1548,6 +1916,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);
 
@@ -1557,6 +1928,59 @@ igt_main
 		igt_subtest("syncobj-wait")
 			test_syncobj_wait(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-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("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

^ permalink raw reply related	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2020-05-13 17:02 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-05-05 22:09 [Intel-gfx] [PATCH i-g-t 1/2] lib/i915: Report scheduler caps for timeslicing Chris Wilson
2020-05-05 22:09 ` [igt-dev] " 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 22:09   ` [igt-dev] " Chris Wilson
2020-05-05 23:27 ` [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-06  9:35 ` [igt-dev] ✗ Fi.CI.IGT: failure " Patchwork
  -- strict thread matches above, loose matches on Subject: below --
2020-05-13 17:02 [Intel-gfx] [PATCH i-g-t 1/2] " Chris Wilson
2020-05-13 17:02 ` [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

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.