All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH i-g-t 01/19] i915/gem_ppgtt: Estimate resource usage and bail if it means swapping!
@ 2019-03-08 18:11 Chris Wilson
  2019-03-08 18:11 ` [PATCH i-g-t 02/19] lib/i915: Pretty print HW semaphores Chris Wilson
                   ` (19 more replies)
  0 siblings, 20 replies; 28+ messages in thread
From: Chris Wilson @ 2019-03-08 18:11 UTC (permalink / raw)
  To: intel-gfx

fi-kbl-guc's swap ran dry while running blt-vs-render-ctxN, which is
midly concerning but conceivable as we never checked there was enough
memory to run the test to begin with.

Each child needs to keep its own surface and possible a pair of logical
contexts (one for rcs and one for bcs) so check that there is enough
memory to allow all children to co-exist. During execution, we require
another surface and batch, but these are temporary and so should fit
fine with a small amount of thrashing on the boundary.

References: https://bugs.freedesktop.org/show_bug.cgi?id=109801
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 tests/i915/gem_ppgtt.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/tests/i915/gem_ppgtt.c b/tests/i915/gem_ppgtt.c
index 11ca31e74..9409bef14 100644
--- a/tests/i915/gem_ppgtt.c
+++ b/tests/i915/gem_ppgtt.c
@@ -91,8 +91,14 @@ static void fork_rcs_copy(int timeout, uint32_t final,
 #define CREATE_CONTEXT 0x1
 {
 	igt_render_copyfunc_t render_copy;
+	uint64_t mem_per_child;
 	int devid;
 
+	mem_per_child = SIZE;
+	if (flags & CREATE_CONTEXT)
+		mem_per_child += 2 * 128 * 1024; /* rough context sizes */
+	intel_require_memory(mem_per_child, count, CHECK_RAM);
+
 	for (int child = 0; child < count; child++) {
 		int fd = drm_open_driver(DRIVER_INTEL);
 		drm_intel_bufmgr *bufmgr;
-- 
2.20.1

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

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

* [PATCH i-g-t 02/19] lib/i915: Pretty print HW semaphores
  2019-03-08 18:11 [PATCH i-g-t 01/19] i915/gem_ppgtt: Estimate resource usage and bail if it means swapping! Chris Wilson
@ 2019-03-08 18:11 ` Chris Wilson
  2019-03-08 18:11 ` [PATCH i-g-t 03/19] lib: Add GPU power measurement Chris Wilson
                   ` (18 subsequent siblings)
  19 siblings, 0 replies; 28+ messages in thread
From: Chris Wilson @ 2019-03-08 18:11 UTC (permalink / raw)
  To: intel-gfx

Include whether the scheduler is using HW semaphore assistance in our
pretty debug strings, and make the caps known for requires.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 lib/i915/gem_scheduler.c | 22 +++++++++++++++++++---
 lib/i915/gem_scheduler.h |  2 ++
 2 files changed, 21 insertions(+), 3 deletions(-)

diff --git a/lib/i915/gem_scheduler.c b/lib/i915/gem_scheduler.c
index ad156306f..f9e052278 100644
--- a/lib/i915/gem_scheduler.c
+++ b/lib/i915/gem_scheduler.c
@@ -67,7 +67,7 @@ unsigned gem_scheduler_capability(int fd)
 }
 
 /**
- * gem_has_scheduler:
+ * gem_scheduler_enabled:
  * @fd: open i915 drm file descriptor
  *
  * Feature test macro to query whether the driver has scheduling capability.
@@ -79,7 +79,7 @@ bool gem_scheduler_enabled(int fd)
 }
 
 /**
- * gem_has_ctx_priority:
+ * gem_scheduler_has_ctx_priority:
  * @fd: open i915 drm file descriptor
  *
  * Feature test macro to query whether the driver supports assigning custom
@@ -92,7 +92,7 @@ bool gem_scheduler_has_ctx_priority(int fd)
 }
 
 /**
- * gem_has_preemption:
+ * gem_scheduler_has_preemption:
  * @fd: open i915 drm file descriptor
  *
  * Feature test macro to query whether the driver supports preempting active
@@ -104,6 +104,20 @@ bool gem_scheduler_has_preemption(int fd)
 	       LOCAL_I915_SCHEDULER_CAP_PREEMPTION;
 }
 
+/**
+ * gem_scheduler_has_semaphores:
+ * @fd: open i915 drm file descriptor
+ *
+ * Feature test macro to query whether the driver supports using HW semaphores
+ * to schedule dependencies in parallel (using the HW to delay execution until
+ * ready to reduce latency).
+ */
+bool gem_scheduler_has_semaphores(int fd)
+{
+	return gem_scheduler_capability(fd) &
+	       LOCAL_I915_SCHEDULER_CAP_SEMAPHORES;
+}
+
 /**
  * gem_scheduler_print_capability:
  * @fd: open i915 drm file descriptor
@@ -122,4 +136,6 @@ void gem_scheduler_print_capability(int fd)
 		igt_info(" - With priority sorting\n");
 	if (caps & LOCAL_I915_SCHEDULER_CAP_PREEMPTION)
 		igt_info(" - With preemption enabled\n");
+	if (caps & LOCAL_I915_SCHEDULER_CAP_SEMAPHORES)
+		igt_info(" - With HW semaphores enabled\n");
 }
diff --git a/lib/i915/gem_scheduler.h b/lib/i915/gem_scheduler.h
index 9fcb02665..ead3eacb5 100644
--- a/lib/i915/gem_scheduler.h
+++ b/lib/i915/gem_scheduler.h
@@ -27,11 +27,13 @@
 #define LOCAL_I915_SCHEDULER_CAP_ENABLED	(1 << 0)
 #define LOCAL_I915_SCHEDULER_CAP_PRIORITY	(1 << 1)
 #define LOCAL_I915_SCHEDULER_CAP_PREEMPTION	(1 << 2)
+#define LOCAL_I915_SCHEDULER_CAP_SEMAPHORES	(1 << 3)
 
 unsigned gem_scheduler_capability(int fd);
 bool gem_scheduler_enabled(int fd);
 bool gem_scheduler_has_ctx_priority(int fd);
 bool gem_scheduler_has_preemption(int fd);
+bool gem_scheduler_has_semaphores(int fd);
 void gem_scheduler_print_capability(int fd);
 
 #endif /* GEM_SCHEDULER_H */
-- 
2.20.1

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

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

* [PATCH i-g-t 03/19] lib: Add GPU power measurement
  2019-03-08 18:11 [PATCH i-g-t 01/19] i915/gem_ppgtt: Estimate resource usage and bail if it means swapping! Chris Wilson
  2019-03-08 18:11 ` [PATCH i-g-t 02/19] lib/i915: Pretty print HW semaphores Chris Wilson
@ 2019-03-08 18:11 ` Chris Wilson
  2019-03-08 18:11 ` [PATCH i-g-t 04/19] i915/gem_exec_schedule: Measure semaphore power consumption Chris Wilson
                   ` (17 subsequent siblings)
  19 siblings, 0 replies; 28+ messages in thread
From: Chris Wilson @ 2019-03-08 18:11 UTC (permalink / raw)
  To: intel-gfx

Read the RAPL power metrics courtesy of perf. Or your local HW
equivalent?

v2: uselocale()

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 lib/Makefile.sources |   2 +
 lib/igt_gpu_power.c  | 114 +++++++++++++++++++++++++++++++++++++++++++
 lib/igt_gpu_power.h  |  59 ++++++++++++++++++++++
 lib/meson.build      |   2 +
 4 files changed, 177 insertions(+)
 create mode 100644 lib/igt_gpu_power.c
 create mode 100644 lib/igt_gpu_power.h

diff --git a/lib/Makefile.sources b/lib/Makefile.sources
index cf2720981..e00347f94 100644
--- a/lib/Makefile.sources
+++ b/lib/Makefile.sources
@@ -26,6 +26,8 @@ lib_source_list =	 	\
 	igt_color_encoding.c	\
 	igt_color_encoding.h	\
 	igt_edid_template.h	\
+	igt_gpu_power.c		\
+	igt_gpu_power.h		\
 	igt_gt.c		\
 	igt_gt.h		\
 	igt_gvt.c		\
diff --git a/lib/igt_gpu_power.c b/lib/igt_gpu_power.c
new file mode 100644
index 000000000..a4e3c1420
--- /dev/null
+++ b/lib/igt_gpu_power.c
@@ -0,0 +1,114 @@
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <locale.h>
+#include <math.h>
+#include <unistd.h>
+
+#include "igt_gpu_power.h"
+#include "igt_perf.h"
+
+static int filename_to_buf(const char *filename, char *buf, unsigned int sz)
+{
+	int fd;
+	ssize_t ret;
+
+	fd = open(filename, O_RDONLY);
+	if (fd < 0)
+		return -1;
+
+	ret = read(fd, buf, sz - 1);
+	close(fd);
+	if (ret < 1)
+		return -1;
+
+	buf[ret] = '\0';
+
+	return 0;
+}
+
+static uint64_t filename_to_u64(const char *filename, int base)
+{
+	char buf[64], *b;
+
+	if (filename_to_buf(filename, buf, sizeof(buf)))
+		return 0;
+
+	/*
+	 * Handle both single integer and key=value formats by skipping
+	 * leading non-digits.
+	 */
+	b = buf;
+	while (*b && !isdigit(*b))
+		b++;
+
+	return strtoull(b, NULL, base);
+}
+
+static double filename_to_double(const char *filename)
+{
+	locale_t locale, oldlocale;
+	char buf[80];
+	double v;
+
+	if (filename_to_buf(filename, buf, sizeof(buf)))
+		return 0;
+
+	/* Replace user environment with plain C to match kernel format */
+	locale = newlocale(LC_ALL, "C", 0);
+	oldlocale = uselocale(locale);
+
+	v = strtod(buf, NULL);
+
+	uselocale(oldlocale);
+	freelocale(locale);
+
+	return v;
+}
+
+static uint64_t rapl_type_id(void)
+{
+	return filename_to_u64("/sys/devices/power/type", 10);
+}
+
+static uint64_t rapl_gpu_power(void)
+{
+	return filename_to_u64("/sys/devices/power/events/energy-gpu", 0);
+}
+
+static double rapl_gpu_power_scale(void)
+{
+	return filename_to_double("/sys/devices/power/events/energy-gpu.scale");
+}
+
+int gpu_power_open(struct gpu_power *power)
+{
+	power->fd = igt_perf_open(rapl_type_id(), rapl_gpu_power());
+	if (power->fd < 0) {
+		power->fd = -errno;
+		goto err;
+	}
+
+	power->scale = rapl_gpu_power_scale();
+	if (isnan(power->scale) || !power->scale) {
+		close(power->fd);
+		goto err;
+	}
+	power->scale *= 1e9;
+
+	return 0;
+
+err:
+	errno = 0;
+	return power->fd;
+}
+
+bool gpu_power_read(struct gpu_power *power, struct gpu_power_sample *s)
+{
+	return read(power->fd, s, sizeof(*s)) == sizeof(*s);
+}
+
+void gpu_power_close(struct gpu_power *power)
+{
+	close(power->fd);
+}
diff --git a/lib/igt_gpu_power.h b/lib/igt_gpu_power.h
new file mode 100644
index 000000000..4e1b747c0
--- /dev/null
+++ b/lib/igt_gpu_power.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright © 2019 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ */
+
+#ifndef IGT_GPU_POWER_H
+#define IGT_GPU_POWER_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+struct gpu_power {
+	int fd;
+	double scale;
+};
+
+struct gpu_power_sample {
+	uint64_t energy;
+	uint64_t time;
+};
+
+int gpu_power_open(struct gpu_power *power);
+bool gpu_power_read(struct gpu_power *power, struct gpu_power_sample *s);
+void gpu_power_close(struct gpu_power *power);
+
+static inline double gpu_power_J(const struct gpu_power *p,
+				 const struct gpu_power_sample *t0,
+				 const struct gpu_power_sample *t1)
+{
+	return (t1->energy - t0->energy) * p->scale * 1e-9;
+}
+
+static inline double gpu_power_W(const struct gpu_power *p,
+				 const struct gpu_power_sample *t0,
+				 const struct gpu_power_sample *t1)
+{
+	return (t1->energy - t0->energy) * p->scale / (t1->time - t0->time);
+}
+
+#endif /* IGT_GPU_POWER_H */
diff --git a/lib/meson.build b/lib/meson.build
index 0eb5585d7..89de06e69 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -9,9 +9,11 @@ lib_sources = [
 	'igt_debugfs.c',
 	'igt_device.c',
 	'igt_aux.c',
+	'igt_gpu_power.c',
 	'igt_gt.c',
 	'igt_gvt.c',
 	'igt_matrix.c',
+	'igt_perf.c',
 	'igt_primes.c',
 	'igt_rand.c',
 	'igt_stats.c',
-- 
2.20.1

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

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

* [PATCH i-g-t 04/19] i915/gem_exec_schedule: Measure semaphore power consumption
  2019-03-08 18:11 [PATCH i-g-t 01/19] i915/gem_ppgtt: Estimate resource usage and bail if it means swapping! Chris Wilson
  2019-03-08 18:11 ` [PATCH i-g-t 02/19] lib/i915: Pretty print HW semaphores Chris Wilson
  2019-03-08 18:11 ` [PATCH i-g-t 03/19] lib: Add GPU power measurement Chris Wilson
@ 2019-03-08 18:11 ` Chris Wilson
  2019-03-08 18:11 ` [PATCH i-g-t 05/19] i915/gem_exec_whisper: Measure total power consumed Chris Wilson
                   ` (16 subsequent siblings)
  19 siblings, 0 replies; 28+ messages in thread
From: Chris Wilson @ 2019-03-08 18:11 UTC (permalink / raw)
  To: intel-gfx

How much energy does spinning on a semaphore consume relative to plain
old spinning?

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

diff --git a/tests/i915/gem_exec_schedule.c b/tests/i915/gem_exec_schedule.c
index a9383000a..4f0577b4e 100644
--- a/tests/i915/gem_exec_schedule.c
+++ b/tests/i915/gem_exec_schedule.c
@@ -29,9 +29,10 @@
 #include <signal.h>
 
 #include "igt.h"
-#include "igt_vgem.h"
+#include "igt_gpu_power.h"
 #include "igt_rand.h"
 #include "igt_sysfs.h"
+#include "igt_vgem.h"
 #include "i915/gem_ring.h"
 
 #define LO 0
@@ -1202,6 +1203,65 @@ static void test_pi_ringfull(int fd, unsigned int engine)
 	munmap(result, 4096);
 }
 
+static void measure_semaphore_power(int i915)
+{
+	struct gpu_power power;
+	unsigned int engine, signaler;
+
+	igt_require(gpu_power_open(&power) == 0);
+
+	for_each_physical_engine(i915, signaler) {
+		struct gpu_power_sample s_spin[2];
+		struct gpu_power_sample s_sema[2];
+		double baseline, total;
+		int64_t jiffie = 1;
+		igt_spin_t *spin;
+
+		spin = __igt_spin_batch_new(i915,
+					    .engine = signaler,
+					    .flags = IGT_SPIN_POLL_RUN);
+		gem_wait(i915, spin->handle, &jiffie); /* waitboost */
+		igt_assert(spin->running);
+		igt_spin_busywait_until_running(spin);
+
+		gpu_power_read(&power, &s_spin[0]);
+		usleep(100*1000);
+		gpu_power_read(&power, &s_spin[1]);
+
+		/* Add a waiter to each engine */
+		for_each_physical_engine(i915, engine) {
+			igt_spin_t *sema;
+
+			if (engine == signaler)
+				continue;
+
+			sema = __igt_spin_batch_new(i915,
+						    .engine = engine,
+						    .dependency = spin->handle);
+
+			igt_spin_batch_free(i915, sema);
+		}
+		usleep(10); /* just give the tasklets a chance to run */
+
+		gpu_power_read(&power, &s_sema[0]);
+		usleep(100*1000);
+		gpu_power_read(&power, &s_sema[1]);
+
+		igt_spin_batch_free(i915, spin);
+
+		baseline = gpu_power_W(&power, &s_spin[0], &s_spin[1]);
+		total = gpu_power_W(&power, &s_sema[0], &s_sema[1]);
+
+		igt_info("%s: %.1fmW + %.1fmW (total %1.fmW)\n",
+			 e__->name,
+			 1e3 * baseline,
+			 1e3 * (total - baseline),
+			 1e3 * total);
+	}
+
+	gpu_power_close(&power);
+}
+
 igt_main
 {
 	const struct intel_execution_engine *e;
@@ -1362,6 +1422,16 @@ igt_main
 		}
 	}
 
+	igt_subtest_group {
+		igt_fixture {
+			igt_require(gem_scheduler_enabled(fd));
+			igt_require(gem_scheduler_has_semaphores(fd));
+		}
+
+		igt_subtest("semaphore-power")
+			measure_semaphore_power(fd);
+	}
+
 	igt_fixture {
 		igt_stop_hang_detector();
 		close(fd);
-- 
2.20.1

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

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

* [PATCH i-g-t 05/19] i915/gem_exec_whisper: Measure total power consumed
  2019-03-08 18:11 [PATCH i-g-t 01/19] i915/gem_ppgtt: Estimate resource usage and bail if it means swapping! Chris Wilson
                   ` (2 preceding siblings ...)
  2019-03-08 18:11 ` [PATCH i-g-t 04/19] i915/gem_exec_schedule: Measure semaphore power consumption Chris Wilson
@ 2019-03-08 18:11 ` Chris Wilson
  2019-03-08 18:11 ` [PATCH i-g-t 06/19] i915/gem_exec_schedule: Verify that using HW semaphores doesn't block Chris Wilson
                   ` (15 subsequent siblings)
  19 siblings, 0 replies; 28+ messages in thread
From: Chris Wilson @ 2019-03-08 18:11 UTC (permalink / raw)
  To: intel-gfx

Show the total power consumed across all the whispers.

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

diff --git a/tests/i915/gem_exec_whisper.c b/tests/i915/gem_exec_whisper.c
index 0b15fe431..6c3b53756 100644
--- a/tests/i915/gem_exec_whisper.c
+++ b/tests/i915/gem_exec_whisper.c
@@ -28,8 +28,9 @@
  */
 
 #include "igt.h"
-#include "igt_gt.h"
 #include "igt_debugfs.h"
+#include "igt_gpu_power.h"
+#include "igt_gt.h"
 #include "igt_rand.h"
 #include "igt_sysfs.h"
 
@@ -192,6 +193,8 @@ static void whisper(int fd, unsigned engine, unsigned flags)
 	unsigned int reloc_migrations = 0;
 	unsigned int reloc_interruptions = 0;
 	unsigned int eb_migrations = 0;
+	struct gpu_power_sample sample[2];
+	struct gpu_power power;
 	uint64_t old_offset;
 	int i, n, loc;
 	int debugfs;
@@ -202,6 +205,7 @@ static void whisper(int fd, unsigned engine, unsigned flags)
 	}
 
 	debugfs = igt_debugfs_dir(fd);
+	gpu_power_open(&power);
 
 	nengine = 0;
 	if (engine == ALL_ENGINES) {
@@ -226,6 +230,7 @@ static void whisper(int fd, unsigned engine, unsigned flags)
 		init_hang(&hang);
 
 	intel_detect_and_clear_missed_interrupts(fd);
+	gpu_power_read(&power, &sample[0]);
 	igt_fork(child, flags & FORKED ? sysconf(_SC_NPROCESSORS_ONLN) : 1)  {
 		unsigned int pass;
 
@@ -495,6 +500,10 @@ static void whisper(int fd, unsigned engine, unsigned flags)
 		fini_hang(&hang);
 	else
 		igt_assert_eq(intel_detect_and_clear_missed_interrupts(fd), 0);
+	if (gpu_power_read(&power, &sample[1]))  {
+		igt_info("Total energy used: %.1fmJ\n",
+			 gpu_power_J(&power, &sample[0], &sample[1]) * 1e3);
+	}
 
 	close(debugfs);
 }
-- 
2.20.1

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

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

* [PATCH i-g-t 06/19] i915/gem_exec_schedule: Verify that using HW semaphores doesn't block
  2019-03-08 18:11 [PATCH i-g-t 01/19] i915/gem_ppgtt: Estimate resource usage and bail if it means swapping! Chris Wilson
                   ` (3 preceding siblings ...)
  2019-03-08 18:11 ` [PATCH i-g-t 05/19] i915/gem_exec_whisper: Measure total power consumed Chris Wilson
@ 2019-03-08 18:11 ` Chris Wilson
  2019-03-08 18:11 ` [PATCH i-g-t 07/19] i915/gem_exec_nop: poll-sequential requires ordering between rings Chris Wilson
                   ` (14 subsequent siblings)
  19 siblings, 0 replies; 28+ messages in thread
From: Chris Wilson @ 2019-03-08 18:11 UTC (permalink / raw)
  To: intel-gfx

We may use HW semaphores to schedule nearly-ready work such that they
are already spinning on the GPU waiting for the completion on another
engine. However, we don't want for that spinning task to actually block
any real work should it be scheduled.

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

diff --git a/tests/i915/gem_exec_schedule.c b/tests/i915/gem_exec_schedule.c
index 4f0577b4e..ae850c4a3 100644
--- a/tests/i915/gem_exec_schedule.c
+++ b/tests/i915/gem_exec_schedule.c
@@ -48,6 +48,10 @@
 
 #define MAX_CONTEXTS 1024
 
+#define LOCAL_I915_EXEC_BSD_SHIFT      (13)
+#define LOCAL_I915_EXEC_BSD_MASK       (3 << LOCAL_I915_EXEC_BSD_SHIFT)
+#define ENGINE_MASK  (I915_EXEC_RING_MASK | LOCAL_I915_EXEC_BSD_MASK)
+
 IGT_TEST_DESCRIPTION("Check that we can control the order of execution");
 
 static inline
@@ -320,6 +324,86 @@ static void smoketest(int fd, unsigned ring, unsigned timeout)
 	}
 }
 
+static uint32_t __batch_create(int i915, uint32_t offset)
+{
+	const uint32_t bbe = MI_BATCH_BUFFER_END;
+	uint32_t handle;
+
+	handle = gem_create(i915, ALIGN(offset + 4, 4096));
+	gem_write(i915, handle, offset, &bbe, sizeof(bbe));
+
+	return handle;
+}
+
+static uint32_t batch_create(int i915)
+{
+	return __batch_create(i915, 0);
+}
+
+static void semaphore_userlock(int i915)
+{
+	struct drm_i915_gem_exec_object2 obj = {
+		.handle = batch_create(i915),
+	};
+	igt_spin_t *spin = NULL;
+	unsigned int engine;
+	uint32_t scratch;
+
+	igt_require(gem_scheduler_has_preemption(i915));
+
+	/*
+	 * Given the use of semaphores to govern parallel submission
+	 * of nearly-ready work to HW, we still want to run actually
+	 * ready work immediately. Without semaphores, the dependent
+	 * work wouldn't be submitted so our ready work will run.
+	 */
+
+	scratch = gem_create(i915, 4096);
+	for_each_physical_engine(i915, engine) {
+		if (!spin) {
+			spin = igt_spin_batch_new(i915,
+						  .dependency = scratch,
+						  .engine = engine);
+		} else {
+			typeof(spin->execbuf.flags) saved = spin->execbuf.flags;
+
+			spin->execbuf.flags &= ~ENGINE_MASK;
+			spin->execbuf.flags |= engine;
+
+			gem_execbuf(i915, &spin->execbuf);
+
+			spin->execbuf.flags = saved;
+		}
+	}
+	igt_require(spin);
+	gem_close(i915, scratch);
+
+	/*
+	 * On all dependent engines, the request may be executing (busywaiting
+	 * on a HW semaphore) but it should not prevent any real work from
+	 * taking precedence.
+	 */
+	scratch = gem_context_create(i915);
+	for_each_physical_engine(i915, engine) {
+		struct drm_i915_gem_execbuffer2 execbuf = {
+			.buffers_ptr = to_user_pointer(&obj),
+			.buffer_count = 1,
+			.flags = engine,
+			.rsvd1 = scratch,
+		};
+
+		if (engine == (spin->execbuf.flags & ENGINE_MASK))
+			continue;
+
+		gem_execbuf(i915, &execbuf);
+	}
+	gem_context_destroy(i915, scratch);
+	gem_sync(i915, obj.handle); /* to hang unless we can preempt */
+	gem_close(i915, obj.handle);
+
+	igt_spin_batch_free(i915, spin);
+}
+
 static void reorder(int fd, unsigned ring, unsigned flags)
 #define EQUAL 1
 {
@@ -1307,6 +1391,9 @@ igt_main
 			igt_require(gem_scheduler_has_ctx_priority(fd));
 		}
 
+		igt_subtest("semaphore-user")
+			semaphore_userlock(fd);
+
 		igt_subtest("smoketest-all")
 			smoketest(fd, ALL_ENGINES, 30);
 
-- 
2.20.1

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

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

* [PATCH i-g-t 07/19] i915/gem_exec_nop: poll-sequential requires ordering between rings
  2019-03-08 18:11 [PATCH i-g-t 01/19] i915/gem_ppgtt: Estimate resource usage and bail if it means swapping! Chris Wilson
                   ` (4 preceding siblings ...)
  2019-03-08 18:11 ` [PATCH i-g-t 06/19] i915/gem_exec_schedule: Verify that using HW semaphores doesn't block Chris Wilson
@ 2019-03-08 18:11 ` Chris Wilson
  2019-03-08 18:11 ` [PATCH i-g-t 08/19] i915/gem_sync: Make switch-default asymmetric Chris Wilson
                   ` (13 subsequent siblings)
  19 siblings, 0 replies; 28+ messages in thread
From: Chris Wilson @ 2019-03-08 18:11 UTC (permalink / raw)
  To: intel-gfx

In order to correctly serialise the order of execution between rings, we
need to flag the scratch address as being written. Make it so.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 tests/i915/gem_exec_nop.c | 152 +++++++++++++++++++++++++++++++++-----
 1 file changed, 133 insertions(+), 19 deletions(-)

diff --git a/tests/i915/gem_exec_nop.c b/tests/i915/gem_exec_nop.c
index 59a08ad08..b91b4d0f6 100644
--- a/tests/i915/gem_exec_nop.c
+++ b/tests/i915/gem_exec_nop.c
@@ -104,7 +104,7 @@ static double nop_on_ring(int fd, uint32_t handle, unsigned ring_id,
 	return elapsed(&start, &now);
 }
 
-static void poll_ring(int fd, unsigned ring, const char *name, int timeout)
+static void poll_ring(int fd, unsigned engine, const char *name, int timeout)
 {
 	const int gen = intel_gen(intel_get_drm_devid(fd));
 	const uint32_t MI_ARB_CHK = 0x5 << 23;
@@ -112,29 +112,17 @@ static void poll_ring(int fd, unsigned ring, const char *name, int timeout)
 	struct drm_i915_gem_exec_object2 obj;
 	struct drm_i915_gem_relocation_entry reloc[4], *r;
 	uint32_t *bbe[2], *state, *batch;
-	unsigned engines[16], nengine, flags;
 	struct timespec tv = {};
 	unsigned long cycles;
+	unsigned flags;
 	uint64_t elapsed;
 
 	flags = I915_EXEC_NO_RELOC;
 	if (gen == 4 || gen == 5)
 		flags |= I915_EXEC_SECURE;
 
-	nengine = 0;
-	if (ring == ALL_ENGINES) {
-		for_each_physical_engine(fd, ring) {
-			if (!gem_can_store_dword(fd, ring))
-				continue;
-
-			engines[nengine++] = ring;
-		}
-	} else {
-		gem_require_ring(fd, ring);
-		igt_require(gem_can_store_dword(fd, ring));
-		engines[nengine++] = ring;
-	}
-	igt_require(nengine);
+	gem_require_ring(fd, engine);
+	igt_require(gem_can_store_dword(fd, engine));
 
 	memset(&obj, 0, sizeof(obj));
 	obj.handle = gem_create(fd, 4096);
@@ -198,7 +186,7 @@ static void poll_ring(int fd, unsigned ring, const char *name, int timeout)
 	memset(&execbuf, 0, sizeof(execbuf));
 	execbuf.buffers_ptr = to_user_pointer(&obj);
 	execbuf.buffer_count = 1;
-	execbuf.flags = engines[0];
+	execbuf.flags = engine | flags;
 
 	cycles = 0;
 	do {
@@ -208,7 +196,6 @@ static void poll_ring(int fd, unsigned ring, const char *name, int timeout)
 		execbuf.batch_start_offset =
 			(bbe[idx] - batch) * sizeof(*batch) - 64;
 
-		execbuf.flags = engines[cycles % nengine] | flags;
 		gem_execbuf(fd, &execbuf);
 
 		*bbe[!idx] = MI_BATCH_BUFFER_END;
@@ -227,6 +214,133 @@ static void poll_ring(int fd, unsigned ring, const char *name, int timeout)
 	gem_close(fd, obj.handle);
 }
 
+static void poll_sequential(int fd, const char *name, int timeout)
+{
+	const int gen = intel_gen(intel_get_drm_devid(fd));
+	const uint32_t MI_ARB_CHK = 0x5 << 23;
+	struct drm_i915_gem_execbuffer2 execbuf;
+	struct drm_i915_gem_exec_object2 obj[2];
+	struct drm_i915_gem_relocation_entry reloc[4], *r;
+	uint32_t *bbe[2], *state, *batch;
+	unsigned engines[16], nengine, engine, flags;
+	struct timespec tv = {};
+	unsigned long cycles;
+	uint64_t elapsed;
+	bool cached;
+
+	flags = I915_EXEC_NO_RELOC;
+	if (gen == 4 || gen == 5)
+		flags |= I915_EXEC_SECURE;
+
+	nengine = 0;
+	for_each_physical_engine(fd, engine) {
+		if (!gem_can_store_dword(fd, engine))
+			continue;
+
+		engines[nengine++] = engine;
+	}
+	igt_require(nengine);
+
+	memset(obj, 0, sizeof(obj));
+	obj[0].handle = gem_create(fd, 4096);
+	obj[0].flags = EXEC_OBJECT_WRITE;
+	cached = __gem_set_caching(fd, obj[0].handle, 1) == 0;
+	obj[1].handle = gem_create(fd, 4096);
+	obj[1].relocs_ptr = to_user_pointer(reloc);
+	obj[1].relocation_count = ARRAY_SIZE(reloc);
+
+	r = memset(reloc, 0, sizeof(reloc));
+	batch = gem_mmap__wc(fd, obj[1].handle, 0, 4096, PROT_WRITE);
+
+	for (unsigned int start_offset = 0;
+	     start_offset <= 128;
+	     start_offset += 128) {
+		uint32_t *b = batch + start_offset / sizeof(*batch);
+
+		r->target_handle = obj[0].handle;
+		r->offset = (b - batch + 1) * sizeof(uint32_t);
+		r->delta = 0;
+		r->read_domains = I915_GEM_DOMAIN_RENDER;
+		r->write_domain = I915_GEM_DOMAIN_RENDER;
+
+		*b = MI_STORE_DWORD_IMM | (gen < 6 ? 1 << 22 : 0);
+		if (gen >= 8) {
+			*++b = r->delta;
+			*++b = 0;
+		} else if (gen >= 4) {
+			r->offset += sizeof(uint32_t);
+			*++b = 0;
+			*++b = r->delta;
+		} else {
+			*b -= 1;
+			*++b = r->delta;
+		}
+		*++b = start_offset != 0;
+		r++;
+
+		b = batch + (start_offset + 64) / sizeof(*batch);
+		bbe[start_offset != 0] = b;
+		*b++ = MI_ARB_CHK;
+
+		r->target_handle = obj[1].handle;
+		r->offset = (b - batch + 1) * sizeof(uint32_t);
+		r->read_domains = I915_GEM_DOMAIN_COMMAND;
+		r->delta = start_offset + 64;
+		if (gen >= 8) {
+			*b++ = MI_BATCH_BUFFER_START | 1 << 8 | 1;
+			*b++ = r->delta;
+			*b++ = 0;
+		} else if (gen >= 6) {
+			*b++ = MI_BATCH_BUFFER_START | 1 << 8;
+			*b++ = r->delta;
+		} else {
+			*b++ = MI_BATCH_BUFFER_START | 2 << 6;
+			if (gen < 4)
+				r->delta |= 1;
+			*b++ = r->delta;
+		}
+		r++;
+	}
+	igt_assert(r == reloc + ARRAY_SIZE(reloc));
+
+	if (cached)
+		state = gem_mmap__cpu(fd, obj[0].handle, 0, 4096, PROT_READ);
+	else
+		state = gem_mmap__wc(fd, obj[0].handle, 0, 4096, PROT_READ);
+
+	memset(&execbuf, 0, sizeof(execbuf));
+	execbuf.buffers_ptr = to_user_pointer(obj);
+	execbuf.buffer_count = ARRAY_SIZE(obj);
+
+	cycles = 0;
+	do {
+		unsigned int idx = ++cycles & 1;
+
+		*bbe[idx] = MI_ARB_CHK;
+		execbuf.batch_start_offset =
+			(bbe[idx] - batch) * sizeof(*batch) - 64;
+
+		execbuf.flags = engines[cycles % nengine] | flags;
+		gem_execbuf(fd, &execbuf);
+
+		*bbe[!idx] = MI_BATCH_BUFFER_END;
+		__sync_synchronize();
+
+		while (READ_ONCE(*state) != idx)
+			;
+	} while ((elapsed = igt_nsec_elapsed(&tv)) >> 30 < timeout);
+	*bbe[cycles & 1] = MI_BATCH_BUFFER_END;
+	gem_sync(fd, obj[1].handle);
+
+	igt_info("%s completed %ld cycles: %.3f us\n",
+		 name, cycles, elapsed*1e-3/cycles);
+
+	munmap(state, 4096);
+	munmap(batch, 4096);
+	gem_close(fd, obj[1].handle);
+	gem_close(fd, obj[0].handle);
+}
+
 static void single(int fd, uint32_t handle,
 		   unsigned ring_id, const char *ring_name)
 {
@@ -813,7 +927,7 @@ igt_main
 		}
 
 		igt_subtest("poll-sequential")
-			poll_ring(device, ALL_ENGINES, "Sequential", 20);
+			poll_sequential(device, "Sequential", 20);
 
 		igt_subtest("headless") {
 			/* Requires master for changing display modes */
-- 
2.20.1

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

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

* [PATCH i-g-t 08/19] i915/gem_sync: Make switch-default asymmetric
  2019-03-08 18:11 [PATCH i-g-t 01/19] i915/gem_ppgtt: Estimate resource usage and bail if it means swapping! Chris Wilson
                   ` (5 preceding siblings ...)
  2019-03-08 18:11 ` [PATCH i-g-t 07/19] i915/gem_exec_nop: poll-sequential requires ordering between rings Chris Wilson
@ 2019-03-08 18:11 ` Chris Wilson
  2019-03-08 18:11 ` [PATCH i-g-t 09/19] i915/gem_ctx_param: Remove kneecapping Chris Wilson
                   ` (12 subsequent siblings)
  19 siblings, 0 replies; 28+ messages in thread
From: Chris Wilson @ 2019-03-08 18:11 UTC (permalink / raw)
  To: intel-gfx

To make the demonstration of the cheeky preemption more impactful, make
the second context a nop to contrast the first being 1024
MI_STORE_DWORD_IMM. Then if we execute and wait on the second context
before executing the first, the client latency is even more drastically
reduced.

To more clearly show any effect on wait reordering, measure the
alternative path and present both.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 tests/i915/gem_sync.c | 40 +++++++++++++++++++++++++++++-----------
 1 file changed, 29 insertions(+), 11 deletions(-)

diff --git a/tests/i915/gem_sync.c b/tests/i915/gem_sync.c
index fb209977d..3e4feff32 100644
--- a/tests/i915/gem_sync.c
+++ b/tests/i915/gem_sync.c
@@ -651,7 +651,7 @@ switch_ring(int fd, unsigned ring, int num_children, int timeout)
 			struct drm_i915_gem_relocation_entry reloc[1024];
 			struct drm_i915_gem_execbuffer2 execbuf;
 		} contexts[2];
-		double start, elapsed;
+		double elapsed, baseline;
 		unsigned long cycles;
 
 		for (int i = 0; i < ARRAY_SIZE(contexts); i++) {
@@ -679,7 +679,7 @@ switch_ring(int fd, unsigned ring, int num_children, int timeout)
 			c->object[1].handle = gem_create(fd, sz);
 
 			c->object[1].relocs_ptr = to_user_pointer(c->reloc);
-			c->object[1].relocation_count = 1024;
+			c->object[1].relocation_count = 1024 * i;
 
 			batch = gem_mmap__cpu(fd, c->object[1].handle, 0, sz,
 					PROT_WRITE | PROT_READ);
@@ -688,7 +688,7 @@ switch_ring(int fd, unsigned ring, int num_children, int timeout)
 
 			memset(c->reloc, 0, sizeof(c->reloc));
 			b = batch;
-			for (int r = 0; r < 1024; r++) {
+			for (int r = 0; r < c->object[1].relocation_count; r++) {
 				uint64_t offset;
 
 				c->reloc[r].presumed_offset = c->object[0].offset;
@@ -722,26 +722,44 @@ switch_ring(int fd, unsigned ring, int num_children, int timeout)
 		}
 
 		cycles = 0;
-		elapsed = 0;
-		start = gettime();
-		do {
+		baseline = 0;
+		igt_until_timeout(timeout) {
 			do {
 				double this;
 
-				gem_execbuf(fd, &contexts[0].execbuf);
 				gem_execbuf(fd, &contexts[1].execbuf);
+				gem_execbuf(fd, &contexts[0].execbuf);
 
 				this = gettime();
 				gem_sync(fd, contexts[1].object[1].handle);
-				elapsed += gettime() - this;
+				gem_sync(fd, contexts[0].object[1].handle);
+				baseline += gettime() - this;
+			} while (++cycles & 1023);
+		}
+		baseline /= cycles;
+
+		cycles = 0;
+		elapsed = 0;
+		igt_until_timeout(timeout) {
+			do {
+				double this;
 
+				gem_execbuf(fd, &contexts[1].execbuf);
+				gem_execbuf(fd, &contexts[0].execbuf);
+
+				this = gettime();
 				gem_sync(fd, contexts[0].object[1].handle);
+				elapsed += gettime() - this;
+
+				gem_sync(fd, contexts[1].object[1].handle);
 			} while (++cycles & 1023);
-		} while ((gettime() - start) < timeout);
-		igt_info("%s%sompleted %ld cycles: %.3f us\n",
+		}
+		elapsed /= cycles;
+
+		igt_info("%s%sompleted %ld cycles: %.3f us, baseline %.3f us\n",
 			 names[child % num_engines] ?: "",
 			 names[child % num_engines] ? " c" : "C",
-			 cycles, elapsed*1e6/cycles);
+			 cycles, elapsed*1e6, baseline*1e6);
 
 		for (int i = 0; i < ARRAY_SIZE(contexts); i++) {
 			gem_close(fd, contexts[i].object[1].handle);
-- 
2.20.1

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

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

* [PATCH i-g-t 09/19] i915/gem_ctx_param: Remove kneecapping
  2019-03-08 18:11 [PATCH i-g-t 01/19] i915/gem_ppgtt: Estimate resource usage and bail if it means swapping! Chris Wilson
                   ` (6 preceding siblings ...)
  2019-03-08 18:11 ` [PATCH i-g-t 08/19] i915/gem_sync: Make switch-default asymmetric Chris Wilson
@ 2019-03-08 18:11 ` Chris Wilson
  2019-03-08 18:11 ` [PATCH i-g-t 10/19] i915/gem_exec_big: Add a single shot test Chris Wilson
                   ` (11 subsequent siblings)
  19 siblings, 0 replies; 28+ messages in thread
From: Chris Wilson @ 2019-03-08 18:11 UTC (permalink / raw)
  To: intel-gfx

The invalid set/get tests do not serve the purpose of detecting whether
or not invalid parameters are indeed detect correctly -- simply because
the kernel is the arbiter of what is invalid and this test second
guesses that and is wrong.

The intent of this test was to ensure that we didn't include any holes
in the parameter space that may have been used for nefarious undisclosed
purposes, i.e. the maintainer's job backed up by reviewers.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
---
 tests/i915/gem_ctx_param.c | 16 ----------------
 1 file changed, 16 deletions(-)

diff --git a/tests/i915/gem_ctx_param.c b/tests/i915/gem_ctx_param.c
index acc1e6297..b3f8637df 100644
--- a/tests/i915/gem_ctx_param.c
+++ b/tests/i915/gem_ctx_param.c
@@ -296,22 +296,6 @@ igt_main
 
 	/* I915_CONTEXT_PARAM_SSEU tests are located in gem_ctx_sseu.c */
 
-	/* NOTE: This testcase intentionally tests for the next free parameter
-	 * to catch ABI extensions. Don't "fix" this testcase without adding all
-	 * the tests for the new param first.
-	 */
-	arg.param = I915_CONTEXT_PARAM_SSEU + 1;
-
-	igt_subtest("invalid-param-get") {
-		arg.ctx_id = ctx;
-		igt_assert_eq(__gem_context_get_param(fd, &arg), -EINVAL);
-	}
-
-	igt_subtest("invalid-param-set") {
-		arg.ctx_id = ctx;
-		igt_assert_eq(__gem_context_set_param(fd, &arg), -EINVAL);
-	}
-
 	igt_fixture
 		close(fd);
 }
-- 
2.20.1

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

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

* [PATCH i-g-t 10/19] i915/gem_exec_big: Add a single shot test
  2019-03-08 18:11 [PATCH i-g-t 01/19] i915/gem_ppgtt: Estimate resource usage and bail if it means swapping! Chris Wilson
                   ` (7 preceding siblings ...)
  2019-03-08 18:11 ` [PATCH i-g-t 09/19] i915/gem_ctx_param: Remove kneecapping Chris Wilson
@ 2019-03-08 18:11 ` Chris Wilson
  2019-03-08 18:11 ` [PATCH i-g-t 11/19] kms_fence_pin_leak: Ask for the GPU before use Chris Wilson
                   ` (10 subsequent siblings)
  19 siblings, 0 replies; 28+ messages in thread
From: Chris Wilson @ 2019-03-08 18:11 UTC (permalink / raw)
  To: intel-gfx

CI complains that the exhaustive test of trying every size up to the
limit is too slow, so add a simple test that tries to submit one
extreme batch buffer and check all the relocations land.

Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=105555
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 tests/i915/gem_exec_big.c    | 70 ++++++++++++++++++++++++++++++------
 tests/intel-ci/blacklist.txt |  1 +
 2 files changed, 60 insertions(+), 11 deletions(-)

diff --git a/tests/i915/gem_exec_big.c b/tests/i915/gem_exec_big.c
index a15672f66..015f59e29 100644
--- a/tests/i915/gem_exec_big.c
+++ b/tests/i915/gem_exec_big.c
@@ -71,7 +71,7 @@ static void exec1(int fd, uint32_t handle, uint64_t reloc_ofs, unsigned flags, c
 	gem_exec[0].relocs_ptr = to_user_pointer(gem_reloc);
 	gem_exec[0].alignment = 0;
 	gem_exec[0].offset = 0;
-	gem_exec[0].flags = 0;
+	gem_exec[0].flags = EXEC_OBJECT_SUPPORTS_48B_ADDRESS;
 	gem_exec[0].rsvd1 = 0;
 	gem_exec[0].rsvd2 = 0;
 
@@ -154,12 +154,11 @@ static void execN(int fd, uint32_t handle, uint64_t batch_size, unsigned flags,
 	gem_exec[0].handle = handle;
 	gem_exec[0].relocation_count = nreloc;
 	gem_exec[0].relocs_ptr = to_user_pointer(gem_reloc);
+	gem_exec[0].flags = EXEC_OBJECT_SUPPORTS_48B_ADDRESS;
 
 	memset(&execbuf, 0, sizeof(execbuf));
 	execbuf.buffers_ptr = to_user_pointer(gem_exec);
 	execbuf.buffer_count = 1;
-	execbuf.batch_start_offset = 0;
-	execbuf.batch_len = 8;
 	execbuf.flags = flags;
 
 	/* Avoid hitting slowpaths in the reloc processing which might yield a
@@ -197,16 +196,10 @@ static void execN(int fd, uint32_t handle, uint64_t batch_size, unsigned flags,
 #undef reloc_ofs
 }
 
-igt_simple_main
+static void exhaustive(int fd)
 {
 	uint32_t batch[2] = {MI_BATCH_BUFFER_END};
 	uint64_t batch_size, max, ggtt_max, reloc_ofs;
-	int fd;
-
-	fd = drm_open_driver(DRIVER_INTEL);
-	igt_require_gem(fd);
-
-	use_64bit_relocs = intel_gen(intel_get_drm_devid(fd)) >= 8;
 
 	max = 3 * gem_aperture_size(fd) / 4;
 	ggtt_max = 3 * gem_global_aperture_size(fd) / 4;
@@ -258,6 +251,61 @@ igt_simple_main
 		else
 			batch_size *= 2;
 	}
+}
+
+static void single(int i915)
+{
+	const uint32_t bbe = MI_BATCH_BUFFER_END;
+	uint64_t batch_size, limit;
+	uint32_t handle;
+	void *ptr;
+
+	batch_size = (intel_get_avail_ram_mb() - 128) << 20; /* CI slack */
+	limit = gem_aperture_size(i915) - (256 << 10); /* low pages reserved */
+	if (!gem_uses_full_ppgtt(i915))
+		limit = 3 * limit / 4;
+
+	batch_size = min(batch_size, limit);
+	batch_size = ALIGN(batch_size, 4096);
+	igt_info("Submitting a %'"PRId64"MiB batch, %saperture size %'"PRId64"MiB\n",
+		 batch_size >> 20,
+		 gem_uses_full_ppgtt(i915) ? "" : "shared ",
+		 gem_aperture_size(i915) >> 20);
+	intel_require_memory(1, batch_size, CHECK_RAM);
+
+	handle = gem_create(i915, batch_size);
+	gem_write(i915, handle, 0, &bbe, sizeof(bbe));
+
+	if (!FORCE_PREAD_PWRITE && gem_has_llc(i915))
+		ptr = __gem_mmap__cpu(i915, handle, 0, batch_size, PROT_READ);
+	else if (!FORCE_PREAD_PWRITE && gem_mmap__has_wc(i915))
+		ptr = __gem_mmap__wc(i915, handle, 0, batch_size, PROT_READ);
+	else
+		ptr = NULL;
+
+	execN(i915, handle, batch_size, 0, ptr);
+
+	if (ptr)
+		munmap(ptr, batch_size);
+}
+
+igt_main
+{
+	int i915 = -1;
+
+	igt_fixture {
+		i915 = drm_open_driver(DRIVER_INTEL);
+		igt_require_gem(i915);
+
+		use_64bit_relocs = intel_gen(intel_get_drm_devid(i915)) >= 8;
+	}
+
+	igt_subtest("single")
+		single(i915);
+
+	igt_subtest("exhaustive")
+		exhaustive(i915);
 
-	close(fd);
+	igt_fixture
+		close(i915);
 }
diff --git a/tests/intel-ci/blacklist.txt b/tests/intel-ci/blacklist.txt
index a2101ae71..ae61efe3f 100644
--- a/tests/intel-ci/blacklist.txt
+++ b/tests/intel-ci/blacklist.txt
@@ -28,6 +28,7 @@ igt@gem_ctx_thrash(@.*)?
 igt@gem_evict_alignment(@.*)?
 igt@gem_evict_everything(@.*)?
 igt@gem_exec_alignment@(?!.*single).*
+igt@gem_exec_big@(?!.*single).*
 igt@gem_exec_capture@many-(?!4K-).*
 igt@gem_exec_fence@(?!.*basic).*
 igt@gem_exec_flush@(?!.*basic).*
-- 
2.20.1

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

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

* [PATCH i-g-t 11/19] kms_fence_pin_leak: Ask for the GPU before use
  2019-03-08 18:11 [PATCH i-g-t 01/19] i915/gem_ppgtt: Estimate resource usage and bail if it means swapping! Chris Wilson
                   ` (8 preceding siblings ...)
  2019-03-08 18:11 ` [PATCH i-g-t 10/19] i915/gem_exec_big: Add a single shot test Chris Wilson
@ 2019-03-08 18:11 ` Chris Wilson
  2019-03-08 18:11 ` [PATCH i-g-t 12/19] drm-uapi: Import i915_drm.h upto 364df3d04d51 Chris Wilson
                   ` (9 subsequent siblings)
  19 siblings, 0 replies; 28+ messages in thread
From: Chris Wilson @ 2019-03-08 18:11 UTC (permalink / raw)
  To: intel-gfx

Check that the GPU even exists before submitting a batch.

Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=109589
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 tests/kms_fence_pin_leak.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tests/kms_fence_pin_leak.c b/tests/kms_fence_pin_leak.c
index 62c52b627..e6c8b33c3 100644
--- a/tests/kms_fence_pin_leak.c
+++ b/tests/kms_fence_pin_leak.c
@@ -201,6 +201,7 @@ igt_simple_main
 	igt_skip_on_simulation();
 
 	data.drm_fd = drm_open_driver_master(DRIVER_INTEL);
+	igt_require_gem(data.drm_fd);
 
 	data.devid = intel_get_drm_devid(data.drm_fd);
 
-- 
2.20.1

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

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

* [PATCH i-g-t 12/19] drm-uapi: Import i915_drm.h upto 364df3d04d51
  2019-03-08 18:11 [PATCH i-g-t 01/19] i915/gem_ppgtt: Estimate resource usage and bail if it means swapping! Chris Wilson
                   ` (9 preceding siblings ...)
  2019-03-08 18:11 ` [PATCH i-g-t 11/19] kms_fence_pin_leak: Ask for the GPU before use Chris Wilson
@ 2019-03-08 18:11 ` Chris Wilson
  2019-03-08 18:11 ` [PATCH i-g-t 13/19] i915: Exercise creating context with shared GTT Chris Wilson
                   ` (8 subsequent siblings)
  19 siblings, 0 replies; 28+ messages in thread
From: Chris Wilson @ 2019-03-08 18:11 UTC (permalink / raw)
  To: intel-gfx

commit 364df3d04d51f0aad13b898f3dffca8c2d03d2b3 (HEAD)
Author: Chris Wilson <chris@chris-wilson.co.uk>
Date:   Fri Jun 30 13:40:53 2017 +0100

    drm/i915: Allow specification of parallel execbuf

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 include/drm-uapi/i915_drm.h | 352 ++++++++++++++++++++++++++++--------
 1 file changed, 279 insertions(+), 73 deletions(-)

diff --git a/include/drm-uapi/i915_drm.h b/include/drm-uapi/i915_drm.h
index 43fb8ede2..e606680a5 100644
--- a/include/drm-uapi/i915_drm.h
+++ b/include/drm-uapi/i915_drm.h
@@ -62,6 +62,26 @@ extern "C" {
 #define I915_ERROR_UEVENT		"ERROR"
 #define I915_RESET_UEVENT		"RESET"
 
+/*
+ * i915_user_extension: Base class for defining a chain of extensions
+ *
+ * Many interfaces need to grow over time. In most cases we can simply
+ * extend the struct and have userspace pass in more data. Another option,
+ * as demonstrated by Vulkan's approach to providing extensions for forward
+ * and backward compatibility, is to use a list of optional structs to
+ * provide those extra details.
+ *
+ * The key advantage to using an extension chain is that it allows us to
+ * redefine the interface more easily than an ever growing struct of
+ * increasing complexity, and for large parts of that interface to be
+ * entirely optional. The downside is more pointer chasing; chasing across
+ * the boundary with pointers encapsulated inside u64.
+ */
+struct i915_user_extension {
+	__u64 next_extension;
+	__u64 name;
+};
+
 /*
  * MOCS indexes used for GPU surfaces, defining the cacheability of the
  * surface data and the coherency for this data wrt. CPU vs. GPU accesses.
@@ -99,9 +119,14 @@ enum drm_i915_gem_engine_class {
 	I915_ENGINE_CLASS_VIDEO		= 2,
 	I915_ENGINE_CLASS_VIDEO_ENHANCE	= 3,
 
+	/* should be kept compact */
+
 	I915_ENGINE_CLASS_INVALID	= -1
 };
 
+#define I915_ENGINE_CLASS_INVALID_NONE -1
+#define I915_ENGINE_CLASS_INVALID_VIRTUAL 0
+
 /**
  * DOC: perf_events exposed by i915 through /sys/bus/event_sources/drivers/i915
  *
@@ -319,6 +344,9 @@ typedef struct _drm_i915_sarea {
 #define DRM_I915_PERF_ADD_CONFIG	0x37
 #define DRM_I915_PERF_REMOVE_CONFIG	0x38
 #define DRM_I915_QUERY			0x39
+#define DRM_I915_GEM_VM_CREATE		0x3a
+#define DRM_I915_GEM_VM_DESTROY		0x3b
+/* Must be kept compact -- no holes */
 
 #define DRM_IOCTL_I915_INIT		DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t)
 #define DRM_IOCTL_I915_FLUSH		DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH)
@@ -367,6 +395,7 @@ typedef struct _drm_i915_sarea {
 #define DRM_IOCTL_I915_GET_SPRITE_COLORKEY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GET_SPRITE_COLORKEY, struct drm_intel_sprite_colorkey)
 #define DRM_IOCTL_I915_GEM_WAIT		DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_WAIT, struct drm_i915_gem_wait)
 #define DRM_IOCTL_I915_GEM_CONTEXT_CREATE	DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_CREATE, struct drm_i915_gem_context_create)
+#define DRM_IOCTL_I915_GEM_CONTEXT_CREATE_EXT	DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_CREATE, struct drm_i915_gem_context_create_ext)
 #define DRM_IOCTL_I915_GEM_CONTEXT_DESTROY	DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_DESTROY, struct drm_i915_gem_context_destroy)
 #define DRM_IOCTL_I915_REG_READ			DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_REG_READ, struct drm_i915_reg_read)
 #define DRM_IOCTL_I915_GET_RESET_STATS		DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GET_RESET_STATS, struct drm_i915_reset_stats)
@@ -377,6 +406,8 @@ typedef struct _drm_i915_sarea {
 #define DRM_IOCTL_I915_PERF_ADD_CONFIG	DRM_IOW(DRM_COMMAND_BASE + DRM_I915_PERF_ADD_CONFIG, struct drm_i915_perf_oa_config)
 #define DRM_IOCTL_I915_PERF_REMOVE_CONFIG	DRM_IOW(DRM_COMMAND_BASE + DRM_I915_PERF_REMOVE_CONFIG, __u64)
 #define DRM_IOCTL_I915_QUERY			DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_QUERY, struct drm_i915_query)
+#define DRM_IOCTL_I915_GEM_VM_CREATE	DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_VM_CREATE, struct drm_i915_gem_vm_control)
+#define DRM_IOCTL_I915_GEM_VM_DESTROY	DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_VM_DESTROY, struct drm_i915_gem_vm_control)
 
 /* Allow drivers to submit batchbuffers directly to hardware, relying
  * on the security mechanisms provided by hardware.
@@ -476,6 +507,7 @@ typedef struct drm_i915_irq_wait {
 #define   I915_SCHEDULER_CAP_ENABLED	(1ul << 0)
 #define   I915_SCHEDULER_CAP_PRIORITY	(1ul << 1)
 #define   I915_SCHEDULER_CAP_PREEMPTION	(1ul << 2)
+#define   I915_SCHEDULER_CAP_SEMAPHORES	(1ul << 3)
 
 #define I915_PARAM_HUC_STATUS		 42
 
@@ -559,6 +591,14 @@ typedef struct drm_i915_irq_wait {
  */
 #define I915_PARAM_MMAP_GTT_COHERENT	52
 
+/*
+ * Query whether DRM_I915_GEM_EXECBUFFER2 supports coordination of parallel
+ * execution through use of explicit fence support.
+ * See I915_EXEC_FENCE_OUT and I915_EXEC_FENCE_SUBMIT.
+ */
+#define I915_PARAM_HAS_EXEC_SUBMIT_FENCE 53
+/* Must be kept compact -- no holes and well documented */
+
 typedef struct drm_i915_getparam {
 	__s32 param;
 	/*
@@ -574,6 +614,7 @@ typedef struct drm_i915_getparam {
 #define I915_SETPARAM_TEX_LRU_LOG_GRANULARITY             2
 #define I915_SETPARAM_ALLOW_BATCHBUFFER                   3
 #define I915_SETPARAM_NUM_USED_FENCES                     4
+/* Must be kept compact -- no holes */
 
 typedef struct drm_i915_setparam {
 	int param;
@@ -972,7 +1013,7 @@ struct drm_i915_gem_execbuffer2 {
 	 * struct drm_i915_gem_exec_fence *fences.
 	 */
 	__u64 cliprects_ptr;
-#define I915_EXEC_RING_MASK              (7<<0)
+#define I915_EXEC_RING_MASK              (0x3f)
 #define I915_EXEC_DEFAULT                (0<<0)
 #define I915_EXEC_RENDER                 (1<<0)
 #define I915_EXEC_BSD                    (2<<0)
@@ -1078,7 +1119,16 @@ struct drm_i915_gem_execbuffer2 {
  */
 #define I915_EXEC_FENCE_ARRAY   (1<<19)
 
-#define __I915_EXEC_UNKNOWN_FLAGS (-(I915_EXEC_FENCE_ARRAY<<1))
+/*
+ * Setting I915_EXEC_FENCE_SUBMIT implies that lower_32_bits(rsvd2) represent
+ * a sync_file fd to wait upon (in a nonblocking manner) prior to executing
+ * the batch.
+ *
+ * Returns -EINVAL if the sync_file fd cannot be found.
+ */
+#define I915_EXEC_FENCE_SUBMIT		(1 << 20)
+
+#define __I915_EXEC_UNKNOWN_FLAGS (-(I915_EXEC_FENCE_SUBMIT << 1))
 
 #define I915_EXEC_CONTEXT_ID_MASK	(0xffffffff)
 #define i915_execbuffer2_set_context_id(eb2, context) \
@@ -1120,32 +1170,34 @@ struct drm_i915_gem_busy {
 	 * as busy may become idle before the ioctl is completed.
 	 *
 	 * Furthermore, if the object is busy, which engine is busy is only
-	 * provided as a guide. There are race conditions which prevent the
-	 * report of which engines are busy from being always accurate.
-	 * However, the converse is not true. If the object is idle, the
-	 * result of the ioctl, that all engines are idle, is accurate.
+	 * provided as a guide and only indirectly by reporting its class
+	 * (there may be more than one engine in each class). There are race
+	 * conditions which prevent the report of which engines are busy from
+	 * being always accurate.  However, the converse is not true. If the
+	 * object is idle, the result of the ioctl, that all engines are idle,
+	 * is accurate.
 	 *
 	 * The returned dword is split into two fields to indicate both
-	 * the engines on which the object is being read, and the
-	 * engine on which it is currently being written (if any).
+	 * the engine classess on which the object is being read, and the
+	 * engine class on which it is currently being written (if any).
 	 *
 	 * The low word (bits 0:15) indicate if the object is being written
 	 * to by any engine (there can only be one, as the GEM implicit
 	 * synchronisation rules force writes to be serialised). Only the
-	 * engine for the last write is reported.
+	 * engine class (offset by 1, I915_ENGINE_CLASS_RENDER is reported as
+	 * 1 not 0 etc) for the last write is reported.
 	 *
-	 * The high word (bits 16:31) are a bitmask of which engines are
-	 * currently reading from the object. Multiple engines may be
+	 * The high word (bits 16:31) are a bitmask of which engines classes
+	 * are currently reading from the object. Multiple engines may be
 	 * reading from the object simultaneously.
 	 *
-	 * The value of each engine is the same as specified in the
-	 * EXECBUFFER2 ioctl, i.e. I915_EXEC_RENDER, I915_EXEC_BSD etc.
-	 * Note I915_EXEC_DEFAULT is a symbolic value and is mapped to
-	 * the I915_EXEC_RENDER engine for execution, and so it is never
+	 * The value of each engine class is the same as specified in the
+	 * I915_CONTEXT_SET_ENGINES parameter and via perf, i.e.
+	 * I915_ENGINE_CLASS_RENDER, I915_ENGINE_CLASS_COPY, etc.
 	 * reported as active itself. Some hardware may have parallel
 	 * execution engines, e.g. multiple media engines, which are
-	 * mapped to the same identifier in the EXECBUFFER2 ioctl and
-	 * so are not separately reported for busyness.
+	 * mapped to the same class identifier and so are not separately
+	 * reported for busyness.
 	 *
 	 * Caveat emptor:
 	 * Only the boolean result of this query is reliable; that is whether
@@ -1412,65 +1464,18 @@ struct drm_i915_gem_wait {
 };
 
 struct drm_i915_gem_context_create {
-	/*  output: id of new context*/
-	__u32 ctx_id;
-	__u32 pad;
-};
-
-struct drm_i915_gem_context_destroy {
-	__u32 ctx_id;
-	__u32 pad;
-};
-
-struct drm_i915_reg_read {
-	/*
-	 * Register offset.
-	 * For 64bit wide registers where the upper 32bits don't immediately
-	 * follow the lower 32bits, the offset of the lower 32bits must
-	 * be specified
-	 */
-	__u64 offset;
-#define I915_REG_READ_8B_WA (1ul << 0)
-
-	__u64 val; /* Return value */
-};
-/* Known registers:
- *
- * Render engine timestamp - 0x2358 + 64bit - gen7+
- * - Note this register returns an invalid value if using the default
- *   single instruction 8byte read, in order to workaround that pass
- *   flag I915_REG_READ_8B_WA in offset field.
- *
- */
-
-struct drm_i915_reset_stats {
-	__u32 ctx_id;
-	__u32 flags;
-
-	/* All resets since boot/module reload, for all contexts */
-	__u32 reset_count;
-
-	/* Number of batches lost when active in GPU, for this context */
-	__u32 batch_active;
-
-	/* Number of batches lost pending for execution, for this context */
-	__u32 batch_pending;
-
+	__u32 ctx_id; /* output: id of new context*/
 	__u32 pad;
 };
 
-struct drm_i915_gem_userptr {
-	__u64 user_ptr;
-	__u64 user_size;
+struct drm_i915_gem_context_create_ext {
+	__u32 ctx_id; /* output: id of new context*/
 	__u32 flags;
-#define I915_USERPTR_READ_ONLY 0x1
-#define I915_USERPTR_UNSYNCHRONIZED 0x80000000
-	/**
-	 * Returned handle for the object.
-	 *
-	 * Object handles are nonzero.
-	 */
-	__u32 handle;
+#define I915_CONTEXT_CREATE_FLAGS_USE_EXTENSIONS	(1u << 0)
+#define I915_CONTEXT_CREATE_FLAGS_SINGLE_TIMELINE	(1u << 1)
+#define I915_CONTEXT_CREATE_FLAGS_UNKNOWN \
+	(-(I915_CONTEXT_CREATE_FLAGS_SINGLE_TIMELINE << 1))
+	__u64 extensions;
 };
 
 struct drm_i915_gem_context_param {
@@ -1511,6 +1516,43 @@ struct drm_i915_gem_context_param {
  * On creation, all new contexts are marked as recoverable.
  */
 #define I915_CONTEXT_PARAM_RECOVERABLE	0x8
+
+	/*
+	 * The id of the associated virtual memory address space (ppGTT) of
+	 * this context. Can be retrieved and passed to another context
+	 * (on the same fd) for both to use the same ppGTT and so share
+	 * address layouts, and avoid reloading the page tables on context
+	 * switches between themselves.
+	 *
+	 * See DRM_I915_GEM_VM_CREATE and DRM_I915_GEM_VM_DESTROY.
+	 */
+#define I915_CONTEXT_PARAM_VM		0x9
+
+/*
+ * I915_CONTEXT_PARAM_ENGINES:
+ *
+ * Bind this context to operate on this subset of available engines. Henceforth,
+ * the I915_EXEC_RING selector for DRM_IOCTL_I915_GEM_EXECBUFFER2 operates as
+ * an index into this array of engines; I915_EXEC_DEFAULT selecting engine[0]
+ * and upwards. Slots 0...N are filled in using the specified (class, instance).
+ * Use
+ *	engine_class: I915_ENGINE_CLASS_INVALID,
+ *	engine_instance: I915_ENGINE_CLASS_INVALID_NONE
+ * to specify a gap in the array that can be filled in later, e.g. by a
+ * virtual engine used for load balancing.
+ *
+ * Setting the number of engines bound to the context to 0, by passing a zero
+ * sized argument, will revert back to default settings.
+ *
+ * See struct i915_context_param_engines.
+ *
+ * Extensions:
+ *   i915_context_engines_load_balance (I915_CONTEXT_ENGINES_EXT_LOAD_BALANCE)
+ *   i915_context_engines_bond (I915_CONTEXT_ENGINES_EXT_BOND)
+ */
+#define I915_CONTEXT_PARAM_ENGINES	0xa
+/* Must be kept compact -- no holes and well documented */
+
 	__u64 value;
 };
 
@@ -1543,9 +1585,10 @@ struct drm_i915_gem_context_param_sseu {
 	__u16 engine_instance;
 
 	/*
-	 * Unused for now. Must be cleared to zero.
+	 * Unknown flags must be cleared to zero.
 	 */
 	__u32 flags;
+#define I915_CONTEXT_SSEU_FLAG_ENGINE_INDEX (1u << 0)
 
 	/*
 	 * Mask of slices to enable for the context. Valid values are a subset
@@ -1573,6 +1616,168 @@ struct drm_i915_gem_context_param_sseu {
 	__u32 rsvd;
 };
 
+/*
+ * i915_context_engines_load_balance:
+ *
+ * Enable load balancing across this set of engines.
+ *
+ * Into the I915_EXEC_DEFAULT slot [0], a virtual engine is created that when
+ * used will proxy the execbuffer request onto one of the set of engines
+ * in such a way as to distribute the load evenly across the set.
+ *
+ * The set of engines must be compatible (e.g. the same HW class) as they
+ * will share the same logical GPU context and ring.
+ *
+ * To intermix rendering with the virtual engine and direct rendering onto
+ * the backing engines (bypassing the load balancing proxy), the context must
+ * be defined to use a single timeline for all engines.
+ */
+struct i915_context_engines_load_balance {
+	struct i915_user_extension base;
+
+	__u16 engine_index;
+	__u16 mbz16; /* reserved for future use; must be zero */
+	__u32 flags; /* all undefined flags must be zero */
+
+	__u64 engines_mask; /* selection mask of engines[] */
+
+	__u64 mbz64[4]; /* reserved for future use; must be zero */
+};
+
+/*
+ * i915_context_engines_bond:
+ *
+ */
+struct i915_context_engines_bond {
+	struct i915_user_extension base;
+
+	__u16 engine_index;
+	__u16 mbz;
+
+	__u16 master_class;
+	__u16 master_instance;
+
+	__u64 sibling_mask;
+	__u64 flags; /* all undefined flags must be zero */
+};
+
+struct i915_context_param_engines {
+	__u64 extensions; /* linked chain of extension blocks, 0 terminates */
+#define I915_CONTEXT_ENGINES_EXT_LOAD_BALANCE 0
+#define I915_CONTEXT_ENGINES_EXT_BOND 1
+
+	struct {
+		__u16 engine_class; /* see enum drm_i915_gem_engine_class */
+		__u16 engine_instance;
+	} class_instance[0];
+} __attribute__((packed));
+
+#define I915_DEFINE_CONTEXT_PARAM_ENGINES(name__, N__) struct { \
+	__u64 extensions; \
+	struct { \
+		__u16 engine_class; \
+		__u16 engine_instance; \
+	} class_instance[N__]; \
+} __attribute__((packed)) name__
+
+struct drm_i915_gem_context_create_ext_setparam {
+#define I915_CONTEXT_CREATE_EXT_SETPARAM 0
+	struct i915_user_extension base;
+	struct drm_i915_gem_context_param setparam;
+};
+
+struct drm_i915_gem_context_create_ext_clone {
+#define I915_CONTEXT_CREATE_EXT_CLONE 1
+	struct i915_user_extension base;
+	__u32 clone;
+	__u32 flags;
+#define I915_CONTEXT_CLONE_FLAGS	(1u << 0)
+#define I915_CONTEXT_CLONE_SCHED	(1u << 1)
+#define I915_CONTEXT_CLONE_SSEU		(1u << 2)
+#define I915_CONTEXT_CLONE_TIMELINE	(1u << 3)
+#define I915_CONTEXT_CLONE_VM		(1u << 4)
+#define I915_CONTEXT_CLONE_ENGINES	(1u << 5)
+#define I915_CONTEXT_CLONE_UNKNOWN -(I915_CONTEXT_CLONE_ENGINES << 1)
+	__u64 rsvd;
+};
+
+struct drm_i915_gem_context_destroy {
+	__u32 ctx_id;
+	__u32 pad;
+};
+
+/*
+ * DRM_I915_GEM_VM_CREATE -
+ *
+ * Create a new virtual memory address space (ppGTT) for use within a context
+ * on the same file. Extensions can be provided to configure exactly how the
+ * address space is setup upon creation.
+ *
+ * The id of new VM (bound to the fd) for use with I915_CONTEXT_PARAM_VM is
+ * returned.
+ *
+ * DRM_I915_GEM_VM_DESTROY -
+ *
+ * Destroys a previously created VM id.
+ */
+struct drm_i915_gem_vm_control {
+	__u64 extensions;
+	__u32 flags;
+	__u32 id;
+};
+
+struct drm_i915_reg_read {
+	/*
+	 * Register offset.
+	 * For 64bit wide registers where the upper 32bits don't immediately
+	 * follow the lower 32bits, the offset of the lower 32bits must
+	 * be specified
+	 */
+	__u64 offset;
+#define I915_REG_READ_8B_WA (1ul << 0)
+
+	__u64 val; /* Return value */
+};
+
+/* Known registers:
+ *
+ * Render engine timestamp - 0x2358 + 64bit - gen7+
+ * - Note this register returns an invalid value if using the default
+ *   single instruction 8byte read, in order to workaround that pass
+ *   flag I915_REG_READ_8B_WA in offset field.
+ *
+ */
+
+struct drm_i915_reset_stats {
+	__u32 ctx_id;
+	__u32 flags;
+
+	/* All resets since boot/module reload, for all contexts */
+	__u32 reset_count;
+
+	/* Number of batches lost when active in GPU, for this context */
+	__u32 batch_active;
+
+	/* Number of batches lost pending for execution, for this context */
+	__u32 batch_pending;
+
+	__u32 pad;
+};
+
+struct drm_i915_gem_userptr {
+	__u64 user_ptr;
+	__u64 user_size;
+	__u32 flags;
+#define I915_USERPTR_READ_ONLY 0x1
+#define I915_USERPTR_UNSYNCHRONIZED 0x80000000
+	/**
+	 * Returned handle for the object.
+	 *
+	 * Object handles are nonzero.
+	 */
+	__u32 handle;
+};
+
 enum drm_i915_oa_format {
 	I915_OA_FORMAT_A13 = 1,	    /* HSW only */
 	I915_OA_FORMAT_A29,	    /* HSW only */
@@ -1734,6 +1939,7 @@ struct drm_i915_perf_oa_config {
 struct drm_i915_query_item {
 	__u64 query_id;
 #define DRM_I915_QUERY_TOPOLOGY_INFO    1
+/* Must be kept compact -- no holes and well documented */
 
 	/*
 	 * When set to zero by userspace, this is filled with the size of the
-- 
2.20.1

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

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

* [PATCH i-g-t 13/19] i915: Exercise creating context with shared GTT
  2019-03-08 18:11 [PATCH i-g-t 01/19] i915/gem_ppgtt: Estimate resource usage and bail if it means swapping! Chris Wilson
                   ` (10 preceding siblings ...)
  2019-03-08 18:11 ` [PATCH i-g-t 12/19] drm-uapi: Import i915_drm.h upto 364df3d04d51 Chris Wilson
@ 2019-03-08 18:11 ` Chris Wilson
  2019-03-08 18:11 ` [PATCH i-g-t 14/19] igt/gem_ctx_switch: Exercise queues Chris Wilson
                   ` (7 subsequent siblings)
  19 siblings, 0 replies; 28+ messages in thread
From: Chris Wilson @ 2019-03-08 18:11 UTC (permalink / raw)
  To: intel-gfx

v2: Test each shared context is its own timeline and allows request
reordering between shared contexts.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Cc: Mika Kuoppala <mika.kuoppala@linux.intel.com>
Cc: Michal Wajdeczko <michal.wajdeczko@intel.com>
---
 lib/i915/gem_context.c        |  68 +++
 lib/i915/gem_context.h        |  13 +
 tests/Makefile.sources        |   1 +
 tests/i915/gem_ctx_shared.c   | 894 ++++++++++++++++++++++++++++++++++
 tests/i915/gem_exec_whisper.c |  32 +-
 tests/meson.build             |   1 +
 6 files changed, 1000 insertions(+), 9 deletions(-)
 create mode 100644 tests/i915/gem_ctx_shared.c

diff --git a/lib/i915/gem_context.c b/lib/i915/gem_context.c
index 16004685e..3212d529f 100644
--- a/lib/i915/gem_context.c
+++ b/lib/i915/gem_context.c
@@ -275,3 +275,71 @@ void gem_context_set_priority(int fd, uint32_t ctx_id, int prio)
 {
 	igt_assert(__gem_context_set_priority(fd, ctx_id, prio) == 0);
 }
+
+int
+__gem_context_clone(int i915,
+		    uint32_t src, unsigned int share,
+		    unsigned int flags,
+		    uint32_t *out)
+{
+	struct drm_i915_gem_context_create_ext_clone clone = {
+		{ .name = I915_CONTEXT_CREATE_EXT_CLONE },
+		.clone = src,
+		.flags = share,
+	};
+	struct drm_i915_gem_context_create_ext arg = {
+		.flags = flags | I915_CONTEXT_CREATE_FLAGS_USE_EXTENSIONS,
+		.extensions = to_user_pointer(&clone),
+	};
+	int err = 0;
+
+	if (igt_ioctl(i915, DRM_IOCTL_I915_GEM_CONTEXT_CREATE_EXT, &arg))
+		err = -errno;
+
+	*out = arg.ctx_id;
+
+	errno = 0;
+	return err;
+}
+
+static bool __gem_context_has(int i915, uint32_t share, unsigned int flags)
+{
+	uint32_t ctx;
+
+	__gem_context_clone(i915, 0, share, flags, &ctx);
+	if (ctx)
+		gem_context_destroy(i915, ctx);
+
+	errno = 0;
+	return ctx;
+}
+
+bool gem_contexts_has_shared_gtt(int i915)
+{
+	return __gem_context_has(i915, I915_CONTEXT_CLONE_VM, 0);
+}
+
+bool gem_has_queues(int i915)
+{
+	return __gem_context_has(i915,
+				 I915_CONTEXT_CLONE_VM,
+				 I915_CONTEXT_CREATE_FLAGS_SINGLE_TIMELINE);
+}
+
+uint32_t gem_context_clone(int i915,
+			   uint32_t src, unsigned int share,
+			   unsigned int flags)
+{
+	uint32_t ctx;
+
+	igt_assert_eq(__gem_context_clone(i915, src, share, flags, &ctx), 0);
+
+	return ctx;
+}
+
+uint32_t gem_queue_create(int i915)
+{
+	return gem_context_clone(i915, 0,
+				 I915_CONTEXT_CLONE_VM,
+				 I915_CONTEXT_CREATE_FLAGS_SINGLE_TIMELINE);
+}
diff --git a/lib/i915/gem_context.h b/lib/i915/gem_context.h
index aef68dda6..ce2617980 100644
--- a/lib/i915/gem_context.h
+++ b/lib/i915/gem_context.h
@@ -29,6 +29,19 @@ int __gem_context_create(int fd, uint32_t *ctx_id);
 void gem_context_destroy(int fd, uint32_t ctx_id);
 int __gem_context_destroy(int fd, uint32_t ctx_id);
 
+int __gem_context_clone(int i915,
+			uint32_t src, unsigned int share,
+			unsigned int flags,
+			uint32_t *out);
+uint32_t gem_context_clone(int i915,
+			   uint32_t src, unsigned int share,
+			   unsigned int flags);
+
+uint32_t gem_queue_create(int i915);
+
+bool gem_contexts_has_shared_gtt(int i915);
+bool gem_has_queues(int i915);
+
 bool gem_has_contexts(int fd);
 void gem_require_contexts(int fd);
 void gem_context_require_bannable(int fd);
diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index 71ccf00af..9e0dab02e 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -21,6 +21,7 @@ TESTS_progs = \
 	drm_import_export \
 	drm_mm \
 	drm_read \
+	i915/gem_ctx_shared \
 	kms_3d \
 	kms_addfb_basic \
 	kms_atomic \
diff --git a/tests/i915/gem_ctx_shared.c b/tests/i915/gem_ctx_shared.c
new file mode 100644
index 000000000..51e4b2dd6
--- /dev/null
+++ b/tests/i915/gem_ctx_shared.c
@@ -0,0 +1,894 @@
+/*
+ * Copyright © 2017 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ */
+
+#include "igt.h"
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+
+#include <drm.h>
+
+#include "igt_rand.h"
+#include "igt_vgem.h"
+#include "sync_file.h"
+
+#define LO 0
+#define HI 1
+#define NOISE 2
+
+#define MAX_PRIO LOCAL_I915_CONTEXT_MAX_USER_PRIORITY
+#define MIN_PRIO LOCAL_I915_CONTEXT_MIN_USER_PRIORITY
+
+static int priorities[] = {
+	[LO] = MIN_PRIO / 2,
+	[HI] = MAX_PRIO / 2,
+};
+
+#define MAX_ELSP_QLEN 16
+
+IGT_TEST_DESCRIPTION("Test shared contexts.");
+
+static void create_shared_gtt(int i915, unsigned int flags)
+#define DETACHED 0x1
+{
+	const uint32_t bbe = MI_BATCH_BUFFER_END;
+	struct drm_i915_gem_exec_object2 obj = {
+		.handle = gem_create(i915, 4096),
+	};
+	struct drm_i915_gem_execbuffer2 execbuf = {
+		.buffers_ptr = to_user_pointer(&obj),
+		.buffer_count = 1,
+	};
+	uint32_t parent, child;
+
+	gem_write(i915, obj.handle, 0, &bbe, sizeof(bbe));
+	gem_execbuf(i915, &execbuf);
+	gem_sync(i915, obj.handle);
+
+	child = flags & DETACHED ? gem_context_create(i915) : 0;
+	igt_until_timeout(2) {
+		parent = flags & DETACHED ? child : 0;
+		child = gem_context_clone(i915,
+					  parent, I915_CONTEXT_CLONE_VM,
+					  0);
+		execbuf.rsvd1 = child;
+		gem_execbuf(i915, &execbuf);
+
+		if (flags & DETACHED) {
+			gem_context_destroy(i915, parent);
+			gem_execbuf(i915, &execbuf);
+		} else {
+			parent = child;
+			gem_context_destroy(i915, parent);
+		}
+
+		execbuf.rsvd1 = parent;
+		igt_assert_eq(__gem_execbuf(i915, &execbuf), -ENOENT);
+		igt_assert_eq(__gem_context_clone(i915,
+						  parent, I915_CONTEXT_CLONE_VM,
+						  0, &parent), -ENOENT);
+	}
+	if (flags & DETACHED)
+		gem_context_destroy(i915, child);
+
+	gem_sync(i915, obj.handle);
+	gem_close(i915, obj.handle);
+}
+
+static void disjoint_timelines(int i915)
+{
+	const uint32_t bbe = MI_BATCH_BUFFER_END;
+	struct drm_i915_gem_exec_object2 obj = {
+		.handle = gem_create(i915, 4096),
+	};
+	struct drm_i915_gem_execbuffer2 execbuf = {
+		.buffers_ptr = to_user_pointer(&obj),
+		.buffer_count = 1,
+	};
+	struct sync_fence_info parent, child;
+	struct sync_file_info sync_file_info = {
+		.num_fences = 1,
+	};
+
+	/*
+	 * Each context, although they share a vm, are expected to be
+	 * distinct timelines. A request queued to one context should be
+	 * independent of any shared contexts.
+	 *
+	 * This information is exposed via the sync_file->name which
+	 * includes the fence.context.
+	 */
+
+	gem_write(i915, obj.handle, 0, &bbe, sizeof(bbe));
+	gem_execbuf(i915, &execbuf);
+	gem_sync(i915, obj.handle);
+
+	execbuf.flags = I915_EXEC_FENCE_OUT;
+	gem_execbuf_wr(i915, &execbuf);
+	sync_file_info.sync_fence_info = to_user_pointer(&parent);
+	do_ioctl(execbuf.rsvd2 >> 32, SYNC_IOC_FILE_INFO, &sync_file_info);
+	close(execbuf.rsvd2 >> 32);
+
+	execbuf.rsvd1 =
+		gem_context_clone(i915, 0, I915_CONTEXT_CLONE_VM, 0);
+	gem_execbuf_wr(i915, &execbuf);
+	sync_file_info.sync_fence_info = to_user_pointer(&child);
+	do_ioctl(execbuf.rsvd2 >> 32, SYNC_IOC_FILE_INFO, &sync_file_info);
+	close(execbuf.rsvd2 >> 32);
+	gem_context_destroy(i915, execbuf.rsvd1);
+
+	igt_info("Parent fence: %s %s\n", parent.driver_name, parent.obj_name);
+	igt_info("Child fence: %s %s\n", child.driver_name, child.obj_name);
+
+	/* Driver should be the same, but on different timelines */
+	igt_assert(strcmp(parent.driver_name, child.driver_name) == 0);
+	igt_assert(strcmp(parent.obj_name, child.obj_name) != 0);
+
+	gem_sync(i915, obj.handle);
+	gem_close(i915, obj.handle);
+}
+
+static int reopen_driver(int fd)
+{
+	char path[256];
+
+	snprintf(path, sizeof(path), "/proc/self/fd/%d", fd);
+	fd = open(path, O_RDWR);
+	igt_assert_lte(0, fd);
+
+	return fd;
+}
+
+static void exhaust_shared_gtt(int i915, unsigned int flags)
+#define EXHAUST_LRC 0x1
+{
+	i915 = reopen_driver(i915);
+
+	igt_fork(pid, 1) {
+		const uint32_t bbe = MI_BATCH_BUFFER_END;
+		struct drm_i915_gem_exec_object2 obj = {
+			.handle = gem_create(i915, 4096)
+		};
+		struct drm_i915_gem_execbuffer2 execbuf = {
+			.buffers_ptr = to_user_pointer(&obj),
+			.buffer_count = 1,
+		};
+		uint32_t parent, child;
+		unsigned long count = 0;
+		int err;
+
+		gem_write(i915, obj.handle, 0, &bbe, sizeof(bbe));
+
+		child = 0;
+		for (;;) {
+			parent = child;
+			err = __gem_context_clone(i915,
+						  parent, I915_CONTEXT_CLONE_VM,
+						  0, &child);
+			if (err)
+				break;
+
+			if (flags & EXHAUST_LRC) {
+				execbuf.rsvd1 = child;
+				err = __gem_execbuf(i915, &execbuf);
+				if (err)
+					break;
+			}
+
+			count++;
+		}
+		gem_sync(i915, obj.handle);
+
+		igt_info("Created %lu shared contexts, before %d (%s)\n",
+			 count, err, strerror(-err));
+	}
+	close(i915);
+	igt_waitchildren();
+}
+
+static void exec_shared_gtt(int i915, unsigned int ring)
+{
+	const int gen = intel_gen(intel_get_drm_devid(i915));
+	const uint32_t bbe = MI_BATCH_BUFFER_END;
+	struct drm_i915_gem_exec_object2 obj = {
+		.handle = gem_create(i915, 4096)
+	};
+	struct drm_i915_gem_execbuffer2 execbuf = {
+		.buffers_ptr = to_user_pointer(&obj),
+		.buffer_count = 1,
+		.flags = ring,
+	};
+	uint32_t scratch = obj.handle;
+	uint32_t batch[16];
+	int i;
+
+	gem_require_ring(i915, ring);
+	igt_require(gem_can_store_dword(i915, ring));
+
+	/* Load object into place in the GTT */
+	gem_write(i915, obj.handle, 0, &bbe, sizeof(bbe));
+	gem_execbuf(i915, &execbuf);
+
+	/* Presume nothing causes an eviction in the meantime */
+
+	obj.handle = gem_create(i915, 4096);
+
+	i = 0;
+	batch[i] = MI_STORE_DWORD_IMM | (gen < 6 ? 1 << 22 : 0);
+	if (gen >= 8) {
+		batch[++i] = obj.offset;
+		batch[++i] = 0;
+	} else if (gen >= 4) {
+		batch[++i] = 0;
+		batch[++i] = obj.offset;
+	} else {
+		batch[i]--;
+		batch[++i] = obj.offset;
+	}
+	batch[++i] = 0xc0ffee;
+	batch[++i] = MI_BATCH_BUFFER_END;
+	gem_write(i915, obj.handle, 0, batch, sizeof(batch));
+
+	obj.offset += 4096; /* make sure we don't cause an eviction! */
+	obj.flags |= EXEC_OBJECT_PINNED;
+	execbuf.rsvd1 = gem_context_clone(i915, 0, I915_CONTEXT_CLONE_VM, 0);
+	if (gen > 3 && gen < 6)
+		execbuf.flags |= I915_EXEC_SECURE;
+
+	gem_execbuf(i915, &execbuf);
+	gem_context_destroy(i915, execbuf.rsvd1);
+	gem_sync(i915, obj.handle); /* write hazard lies */
+	gem_close(i915, obj.handle);
+
+	gem_read(i915, scratch, 0, batch, sizeof(uint32_t));
+	gem_close(i915, scratch);
+
+	igt_assert_eq_u32(*batch, 0xc0ffee);
+}
+
+static int nop_sync(int i915, uint32_t ctx, unsigned int ring, int64_t timeout)
+{
+	const uint32_t bbe = MI_BATCH_BUFFER_END;
+	struct drm_i915_gem_exec_object2 obj = {
+		.handle = gem_create(i915, 4096),
+	};
+	struct drm_i915_gem_execbuffer2 execbuf = {
+		.buffers_ptr = to_user_pointer(&obj),
+		.buffer_count = 1,
+		.flags = ring,
+		.rsvd1 = ctx,
+	};
+	int err;
+
+	gem_write(i915, obj.handle, 0, &bbe, sizeof(bbe));
+	gem_execbuf(i915, &execbuf);
+	err = gem_wait(i915, obj.handle, &timeout);
+	gem_close(i915, obj.handle);
+
+	return err;
+}
+
+static bool has_single_timeline(int i915)
+{
+	uint32_t ctx;
+
+	__gem_context_clone(i915, 0, 0,
+			    I915_CONTEXT_CREATE_FLAGS_SINGLE_TIMELINE,
+			    &ctx);
+	if (ctx)
+		gem_context_destroy(i915, ctx);
+
+	return ctx != 0;
+}
+
+static bool ignore_engine(unsigned engine)
+{
+	if (engine == 0)
+		return true;
+
+	if (engine == I915_EXEC_BSD)
+		return true;
+
+	return false;
+}
+
+static void single_timeline(int i915)
+{
+	const uint32_t bbe = MI_BATCH_BUFFER_END;
+	struct drm_i915_gem_exec_object2 obj = {
+		.handle = gem_create(i915, 4096),
+	};
+	struct drm_i915_gem_execbuffer2 execbuf = {
+		.buffers_ptr = to_user_pointer(&obj),
+		.buffer_count = 1,
+	};
+	struct sync_fence_info rings[16];
+	struct sync_file_info sync_file_info = {
+		.num_fences = 1,
+	};
+	unsigned int engine;
+	int n;
+
+	igt_require(has_single_timeline(i915));
+
+	gem_write(i915, obj.handle, 0, &bbe, sizeof(bbe));
+	gem_execbuf(i915, &execbuf);
+	gem_sync(i915, obj.handle);
+
+	/*
+	 * For a "single timeline" context, each ring is on the common
+	 * timeline, unlike a normal context where each ring has an
+	 * independent timeline. That is no matter which engine we submit
+	 * to, it reports the same timeline name and fence context. However,
+	 * the fence context is not reported through the sync_fence_info.
+	 */
+	execbuf.rsvd1 =
+		gem_context_clone(i915, 0, 0,
+				  I915_CONTEXT_CREATE_FLAGS_SINGLE_TIMELINE);
+	execbuf.flags = I915_EXEC_FENCE_OUT;
+	n = 0;
+	for_each_engine(i915, engine) {
+		gem_execbuf_wr(i915, &execbuf);
+		sync_file_info.sync_fence_info = to_user_pointer(&rings[n]);
+		do_ioctl(execbuf.rsvd2 >> 32, SYNC_IOC_FILE_INFO, &sync_file_info);
+		close(execbuf.rsvd2 >> 32);
+
+		igt_info("ring[%d] fence: %s %s\n",
+			 n, rings[n].driver_name, rings[n].obj_name);
+		n++;
+	}
+	gem_sync(i915, obj.handle);
+	gem_close(i915, obj.handle);
+
+	for (int i = 1; i < n; i++) {
+		igt_assert(!strcmp(rings[0].driver_name, rings[i].driver_name));
+		igt_assert(!strcmp(rings[0].obj_name, rings[i].obj_name));
+	}
+}
+
+static void exec_single_timeline(int i915, unsigned int ring)
+{
+	unsigned int other;
+	igt_spin_t *spin;
+	uint32_t ctx;
+
+	gem_require_ring(i915, ring);
+	igt_require(has_single_timeline(i915));
+
+	/*
+	 * On an ordinary context, a blockage on one ring doesn't prevent
+	 * execution on an other.
+	 */
+	ctx = 0;
+	spin = NULL;
+	for_each_engine(i915, other) {
+		if (other == ring || ignore_engine(other))
+			continue;
+
+		if (spin == NULL) {
+			spin = __igt_spin_batch_new(i915, ctx, other, 0);
+		} else {
+			struct drm_i915_gem_exec_object2 obj = {
+				.handle = spin->handle,
+			};
+			struct drm_i915_gem_execbuffer2 execbuf = {
+				.buffers_ptr = to_user_pointer(&obj),
+				.buffer_count = 1,
+				.flags = other,
+				.rsvd1 = ctx,
+			};
+			gem_execbuf(i915, &execbuf);
+		}
+	}
+	igt_require(spin);
+	igt_assert_eq(nop_sync(i915, ctx, ring, NSEC_PER_SEC), 0);
+	igt_spin_batch_free(i915, spin);
+
+	/*
+	 * But if we create a context with just a single shared timeline,
+	 * then it will block waiting for the earlier requests on the
+	 * other engines.
+	 */
+	ctx = gem_context_clone(i915, 0, 0,
+				I915_CONTEXT_CREATE_FLAGS_SINGLE_TIMELINE);
+	spin = NULL;
+	for_each_engine(i915, other) {
+		if (other == ring || ignore_engine(other))
+			continue;
+
+		if (spin == NULL) {
+			spin = __igt_spin_batch_new(i915, ctx, other, 0);
+		} else {
+			struct drm_i915_gem_exec_object2 obj = {
+				.handle = spin->handle,
+			};
+			struct drm_i915_gem_execbuffer2 execbuf = {
+				.buffers_ptr = to_user_pointer(&obj),
+				.buffer_count = 1,
+				.flags = other,
+				.rsvd1 = ctx,
+			};
+			gem_execbuf(i915, &execbuf);
+		}
+	}
+	igt_assert(spin);
+	igt_assert_eq(nop_sync(i915, ctx, ring, NSEC_PER_SEC), -ETIME);
+	igt_spin_batch_free(i915, spin);
+}
+
+static void store_dword(int i915, uint32_t ctx, unsigned ring,
+			uint32_t target, uint32_t offset, uint32_t value,
+			uint32_t cork, unsigned write_domain)
+{
+	const int gen = intel_gen(intel_get_drm_devid(i915));
+	struct drm_i915_gem_exec_object2 obj[3];
+	struct drm_i915_gem_relocation_entry reloc;
+	struct drm_i915_gem_execbuffer2 execbuf;
+	uint32_t batch[16];
+	int i;
+
+	memset(&execbuf, 0, sizeof(execbuf));
+	execbuf.buffers_ptr = to_user_pointer(obj + !cork);
+	execbuf.buffer_count = 2 + !!cork;
+	execbuf.flags = ring;
+	if (gen < 6)
+		execbuf.flags |= I915_EXEC_SECURE;
+	execbuf.rsvd1 = ctx;
+
+	memset(obj, 0, sizeof(obj));
+	obj[0].handle = cork;
+	obj[1].handle = target;
+	obj[2].handle = gem_create(i915, 4096);
+
+	memset(&reloc, 0, sizeof(reloc));
+	reloc.target_handle = obj[1].handle;
+	reloc.presumed_offset = 0;
+	reloc.offset = sizeof(uint32_t);
+	reloc.delta = offset;
+	reloc.read_domains = I915_GEM_DOMAIN_INSTRUCTION;
+	reloc.write_domain = write_domain;
+	obj[2].relocs_ptr = to_user_pointer(&reloc);
+	obj[2].relocation_count = 1;
+
+	i = 0;
+	batch[i] = MI_STORE_DWORD_IMM | (gen < 6 ? 1 << 22 : 0);
+	if (gen >= 8) {
+		batch[++i] = offset;
+		batch[++i] = 0;
+	} else if (gen >= 4) {
+		batch[++i] = 0;
+		batch[++i] = offset;
+		reloc.offset += sizeof(uint32_t);
+	} else {
+		batch[i]--;
+		batch[++i] = offset;
+	}
+	batch[++i] = value;
+	batch[++i] = MI_BATCH_BUFFER_END;
+	gem_write(i915, obj[2].handle, 0, batch, sizeof(batch));
+	gem_execbuf(i915, &execbuf);
+	gem_close(i915, obj[2].handle);
+}
+
+static uint32_t create_highest_priority(int i915)
+{
+	uint32_t ctx = gem_context_create(i915);
+
+	/*
+	 * If there is no priority support, all contexts will have equal
+	 * priority (and therefore the max user priority), so no context
+	 * can overtake us, and we effectively can form a plug.
+	 */
+	__gem_context_set_priority(i915, ctx, MAX_PRIO);
+
+	return ctx;
+}
+
+static void unplug_show_queue(int i915, struct igt_cork *c, unsigned int engine)
+{
+	igt_spin_t *spin[MAX_ELSP_QLEN];
+
+	for (int n = 0; n < ARRAY_SIZE(spin); n++) {
+		const struct igt_spin_factory opts = {
+			.ctx = create_highest_priority(i915),
+			.engine = engine,
+		};
+		spin[n] = __igt_spin_batch_factory(i915, &opts);
+		gem_context_destroy(i915, opts.ctx);
+	}
+
+	igt_cork_unplug(c); /* batches will now be queued on the engine */
+	igt_debugfs_dump(i915, "i915_engine_info");
+
+	for (int n = 0; n < ARRAY_SIZE(spin); n++)
+		igt_spin_batch_free(i915, spin[n]);
+}
+
+static uint32_t store_timestamp(int i915,
+				uint32_t ctx, unsigned ring,
+				unsigned mmio_base)
+{
+	const bool r64b = intel_gen(intel_get_drm_devid(i915)) >= 8;
+	struct drm_i915_gem_exec_object2 obj = {
+		.handle = gem_create(i915, 4096),
+		.relocation_count = 1,
+	};
+	struct drm_i915_gem_relocation_entry reloc = {
+		.target_handle = obj.handle,
+		.offset = 2 * sizeof(uint32_t),
+		.delta = 4092,
+		.read_domains = I915_GEM_DOMAIN_INSTRUCTION,
+	};
+	struct drm_i915_gem_execbuffer2 execbuf = {
+		.buffers_ptr = to_user_pointer(&obj),
+		.buffer_count = 1,
+		.flags = ring,
+		.rsvd1 = ctx,
+	};
+	uint32_t batch[] = {
+		0x24 << 23 | (1 + r64b), /* SRM */
+		mmio_base + 0x358,
+		4092,
+		0,
+		MI_BATCH_BUFFER_END
+	};
+
+	igt_require(intel_gen(intel_get_drm_devid(i915)) >= 7);
+
+	gem_write(i915, obj.handle, 0, batch, sizeof(batch));
+	obj.relocs_ptr = to_user_pointer(&reloc);
+
+	gem_execbuf(i915, &execbuf);
+
+	return obj.handle;
+}
+
+static void independent(int i915, unsigned ring, unsigned flags)
+{
+	uint32_t handle[ARRAY_SIZE(priorities)];
+	igt_spin_t *spin[MAX_ELSP_QLEN];
+	unsigned int mmio_base;
+
+	/* XXX i915_query()! */
+	igt_skip_on(intel_gen(intel_get_drm_devid(i915)) >= 11);
+	switch (ring) {
+	case I915_EXEC_DEFAULT:
+	case I915_EXEC_RENDER:
+		mmio_base = 0x2000;
+		break;
+#if 0
+	case I915_EXEC_BSD:
+		mmio_base = 0x12000;
+		break;
+#endif
+	case I915_EXEC_BLT:
+		mmio_base = 0x22000;
+		break;
+	case I915_EXEC_VEBOX:
+		mmio_base = 0x1a000;
+		break;
+
+	default:
+		igt_skip("mmio base not known");
+	}
+
+	for (int n = 0; n < ARRAY_SIZE(spin); n++) {
+		const struct igt_spin_factory opts = {
+			.ctx = create_highest_priority(i915),
+			.engine = ring,
+		};
+		spin[n] = __igt_spin_batch_factory(i915, &opts);
+		gem_context_destroy(i915, opts.ctx);
+	}
+
+	for (int i = 0; i < ARRAY_SIZE(priorities); i++) {
+		uint32_t ctx = gem_queue_create(i915);
+		gem_context_set_priority(i915, ctx, priorities[i]);
+		handle[i] = store_timestamp(i915, ctx, ring, mmio_base);
+		gem_context_destroy(i915, ctx);
+	}
+
+	for (int n = 0; n < ARRAY_SIZE(spin); n++)
+		igt_spin_batch_free(i915, spin[n]);
+
+	for (int i = 0; i < ARRAY_SIZE(priorities); i++) {
+		uint32_t *ptr;
+
+		ptr = gem_mmap__gtt(i915, handle[i], 4096, PROT_READ);
+		gem_set_domain(i915, handle[i], /* no write hazard lies! */
+			       I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
+		gem_close(i915, handle[i]);
+
+		handle[i] = ptr[1023];
+		munmap(ptr, 4096);
+
+		igt_debug("ctx[%d] .prio=%d, timestamp=%u\n",
+			  i, priorities[i], handle[i]);
+	}
+
+	igt_assert((int32_t)(handle[HI] - handle[LO]) < 0);
+}
+
+static void reorder(int i915, unsigned ring, unsigned flags)
+#define EQUAL 1
+{
+	IGT_CORK_HANDLE(cork);
+	uint32_t scratch;
+	uint32_t *ptr;
+	uint32_t ctx[2];
+	uint32_t plug;
+
+	ctx[LO] = gem_queue_create(i915);
+	gem_context_set_priority(i915, ctx[LO], MIN_PRIO);
+
+	ctx[HI] = gem_queue_create(i915);
+	gem_context_set_priority(i915, ctx[HI], flags & EQUAL ? MIN_PRIO : 0);
+
+	scratch = gem_create(i915, 4096);
+	plug = igt_cork_plug(&cork, i915);
+
+	/* We expect the high priority context to be executed first, and
+	 * so the final result will be value from the low priority context.
+	 */
+	store_dword(i915, ctx[LO], ring, scratch, 0, ctx[LO], plug, 0);
+	store_dword(i915, ctx[HI], ring, scratch, 0, ctx[HI], plug, 0);
+
+	unplug_show_queue(i915, &cork, ring);
+	gem_close(i915, plug);
+
+	gem_context_destroy(i915, ctx[LO]);
+	gem_context_destroy(i915, ctx[HI]);
+
+	ptr = gem_mmap__gtt(i915, scratch, 4096, PROT_READ);
+	gem_set_domain(i915, scratch, /* no write hazard lies! */
+		       I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
+	gem_close(i915, scratch);
+
+	if (flags & EQUAL) /* equal priority, result will be fifo */
+		igt_assert_eq_u32(ptr[0], ctx[HI]);
+	else
+		igt_assert_eq_u32(ptr[0], ctx[LO]);
+	munmap(ptr, 4096);
+}
+
+static void promotion(int i915, unsigned ring)
+{
+	IGT_CORK_HANDLE(cork);
+	uint32_t result, dep;
+	uint32_t *ptr;
+	uint32_t ctx[3];
+	uint32_t plug;
+
+	ctx[LO] = gem_queue_create(i915);
+	gem_context_set_priority(i915, ctx[LO], MIN_PRIO);
+
+	ctx[HI] = gem_queue_create(i915);
+	gem_context_set_priority(i915, ctx[HI], 0);
+
+	ctx[NOISE] = gem_queue_create(i915);
+	gem_context_set_priority(i915, ctx[NOISE], MIN_PRIO/2);
+
+	result = gem_create(i915, 4096);
+	dep = gem_create(i915, 4096);
+
+	plug = igt_cork_plug(&cork, i915);
+
+	/* Expect that HI promotes LO, so the order will be LO, HI, NOISE.
+	 *
+	 * fifo would be NOISE, LO, HI.
+	 * strict priority would be  HI, NOISE, LO
+	 */
+	store_dword(i915, ctx[NOISE], ring, result, 0, ctx[NOISE], plug, 0);
+	store_dword(i915, ctx[LO], ring, result, 0, ctx[LO], plug, 0);
+
+	/* link LO <-> HI via a dependency on another buffer */
+	store_dword(i915, ctx[LO], ring, dep, 0, ctx[LO], 0, I915_GEM_DOMAIN_INSTRUCTION);
+	store_dword(i915, ctx[HI], ring, dep, 0, ctx[HI], 0, 0);
+
+	store_dword(i915, ctx[HI], ring, result, 0, ctx[HI], 0, 0);
+
+	unplug_show_queue(i915, &cork, ring);
+	gem_close(i915, plug);
+
+	gem_context_destroy(i915, ctx[NOISE]);
+	gem_context_destroy(i915, ctx[LO]);
+	gem_context_destroy(i915, ctx[HI]);
+
+	ptr = gem_mmap__gtt(i915, dep, 4096, PROT_READ);
+	gem_set_domain(i915, dep, /* no write hazard lies! */
+			I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
+	gem_close(i915, dep);
+
+	igt_assert_eq_u32(ptr[0], ctx[HI]);
+	munmap(ptr, 4096);
+
+	ptr = gem_mmap__gtt(i915, result, 4096, PROT_READ);
+	gem_set_domain(i915, result, /* no write hazard lies! */
+			I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
+	gem_close(i915, result);
+
+	igt_assert_eq_u32(ptr[0], ctx[NOISE]);
+	munmap(ptr, 4096);
+}
+
+static void smoketest(int i915, unsigned ring, unsigned timeout)
+{
+	const int ncpus = sysconf(_SC_NPROCESSORS_ONLN);
+	unsigned engines[16];
+	unsigned nengine;
+	unsigned engine;
+	uint32_t scratch;
+	uint32_t *ptr;
+
+	nengine = 0;
+	for_each_engine(i915, engine) {
+		if (ignore_engine(engine))
+			continue;
+
+		engines[nengine++] = engine;
+	}
+	igt_require(nengine);
+
+	scratch = gem_create(i915, 4096);
+	igt_fork(child, ncpus) {
+		unsigned long count = 0;
+		uint32_t ctx;
+
+		hars_petruska_f54_1_random_perturb(child);
+
+		ctx = gem_queue_create(i915);
+		igt_until_timeout(timeout) {
+			int prio;
+
+			prio = hars_petruska_f54_1_random_unsafe_max(MAX_PRIO - MIN_PRIO) + MIN_PRIO;
+			gem_context_set_priority(i915, ctx, prio);
+
+			engine = engines[hars_petruska_f54_1_random_unsafe_max(nengine)];
+			store_dword(i915, ctx, engine, scratch,
+				    8*child + 0, ~child,
+				    0, 0);
+			for (unsigned int step = 0; step < 8; step++)
+				store_dword(i915, ctx, engine, scratch,
+					    8*child + 4, count++,
+					    0, 0);
+		}
+		gem_context_destroy(i915, ctx);
+	}
+	igt_waitchildren();
+
+	ptr = gem_mmap__gtt(i915, scratch, 4096, PROT_READ);
+	gem_set_domain(i915, scratch, /* no write hazard lies! */
+			I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
+	gem_close(i915, scratch);
+
+	for (unsigned n = 0; n < ncpus; n++) {
+		igt_assert_eq_u32(ptr[2*n], ~n);
+		/*
+		 * Note this count is approximate due to unconstrained
+		 * ordering of the dword writes between engines.
+		 *
+		 * Take the result with a pinch of salt.
+		 */
+		igt_info("Child[%d] completed %u cycles\n",  n, ptr[2*n+1]);
+	}
+	munmap(ptr, 4096);
+}
+
+igt_main
+{
+	const struct intel_execution_engine *e;
+	int i915 = -1;
+
+	igt_fixture {
+		i915 = drm_open_driver(DRIVER_INTEL);
+		igt_require_gem(i915);
+	}
+
+	igt_subtest_group {
+		igt_fixture {
+			igt_require(gem_contexts_has_shared_gtt(i915));
+			igt_fork_hang_detector(i915);
+		}
+
+		igt_subtest("create-shared-gtt")
+			create_shared_gtt(i915, 0);
+
+		igt_subtest("detached-shared-gtt")
+			create_shared_gtt(i915, DETACHED);
+
+		igt_subtest("disjoint-timelines")
+			disjoint_timelines(i915);
+
+		igt_subtest("single-timeline")
+			single_timeline(i915);
+
+		igt_subtest("exhaust-shared-gtt")
+			exhaust_shared_gtt(i915, 0);
+
+		igt_subtest("exhaust-shared-gtt-lrc")
+			exhaust_shared_gtt(i915, EXHAUST_LRC);
+
+		for (e = intel_execution_engines; e->name; e++) {
+			igt_subtest_f("exec-shared-gtt-%s", e->name)
+				exec_shared_gtt(i915, e->exec_id | e->flags);
+
+			if (!ignore_engine(e->exec_id | e->flags)) {
+				igt_subtest_f("exec-single-timeline-%s",
+					      e->name)
+					exec_single_timeline(i915,
+							     e->exec_id | e->flags);
+			}
+
+			/*
+			 * Check that the shared contexts operate independently,
+			 * that is requests on one ("queue") can be scheduled
+			 * around another queue. We only check the basics here,
+			 * enough to reduce the queue into just another context,
+			 * and so rely on gem_exec_schedule to prove the rest.
+			 */
+			igt_subtest_group {
+				igt_fixture {
+					gem_require_ring(i915, e->exec_id | e->flags);
+					igt_require(gem_can_store_dword(i915, e->exec_id) | e->flags);
+					igt_require(gem_scheduler_enabled(i915));
+					igt_require(gem_scheduler_has_ctx_priority(i915));
+				}
+
+				igt_subtest_f("Q-independent-%s", e->name)
+					independent(i915, e->exec_id | e->flags, 0);
+
+				igt_subtest_f("Q-in-order-%s", e->name)
+					reorder(i915, e->exec_id | e->flags, EQUAL);
+
+				igt_subtest_f("Q-out-order-%s", e->name)
+					reorder(i915, e->exec_id | e->flags, 0);
+
+				igt_subtest_f("Q-promotion-%s", e->name)
+					promotion(i915, e->exec_id | e->flags);
+
+				igt_subtest_f("Q-smoketest-%s", e->name)
+					smoketest(i915, e->exec_id | e->flags, 5);
+			}
+		}
+
+		igt_subtest("Q-smoketest-all") {
+			igt_require(gem_scheduler_enabled(i915));
+			igt_require(gem_scheduler_has_ctx_priority(i915));
+			smoketest(i915, -1, 30);
+		}
+
+		igt_fixture {
+			igt_stop_hang_detector();
+		}
+	}
+}
diff --git a/tests/i915/gem_exec_whisper.c b/tests/i915/gem_exec_whisper.c
index 6c3b53756..d3e0b0ba2 100644
--- a/tests/i915/gem_exec_whisper.c
+++ b/tests/i915/gem_exec_whisper.c
@@ -87,6 +87,7 @@ static void verify_reloc(int fd, uint32_t handle,
 #define HANG 0x20
 #define SYNC 0x40
 #define PRIORITY 0x80
+#define QUEUES 0x100
 
 struct hang {
 	struct drm_i915_gem_exec_object2 obj;
@@ -171,7 +172,7 @@ static void ctx_set_random_priority(int fd, uint32_t ctx)
 {
 	int prio = hars_petruska_f54_1_random_unsafe_max(1024) - 512;
 	gem_context_set_priority(fd, ctx, prio);
-};
+}
 
 static void whisper(int fd, unsigned engine, unsigned flags)
 {
@@ -226,6 +227,9 @@ static void whisper(int fd, unsigned engine, unsigned flags)
 	if (flags & CONTEXTS)
 		gem_require_contexts(fd);
 
+	if (flags & QUEUES)
+		igt_require(gem_has_queues(fd));
+
 	if (flags & HANG)
 		init_hang(&hang);
 
@@ -290,6 +294,10 @@ static void whisper(int fd, unsigned engine, unsigned flags)
 			for (n = 0; n < 64; n++)
 				contexts[n] = gem_context_create(fd);
 		}
+		if (flags & QUEUES) {
+			for (n = 0; n < 64; n++)
+				contexts[n] = gem_queue_create(fd);
+		}
 		if (flags & FDS) {
 			for (n = 0; n < 64; n++)
 				fds[n] = drm_open_driver(DRIVER_INTEL);
@@ -403,7 +411,7 @@ static void whisper(int fd, unsigned engine, unsigned flags)
 						execbuf.flags &= ~ENGINE_MASK;
 						execbuf.flags |= engines[rand() % nengine];
 					}
-					if (flags & CONTEXTS) {
+					if (flags & (CONTEXTS | QUEUES)) {
 						execbuf.rsvd1 = contexts[rand() % 64];
 						if (flags & PRIORITY)
 							ctx_set_random_priority(this_fd, execbuf.rsvd1);
@@ -486,7 +494,7 @@ static void whisper(int fd, unsigned engine, unsigned flags)
 			for (n = 0; n < 64; n++)
 				close(fds[n]);
 		}
-		if (flags & CONTEXTS) {
+		if (flags & (CONTEXTS | QUEUES)) {
 			for (n = 0; n < 64; n++)
 				gem_context_destroy(fd, contexts[n]);
 		}
@@ -522,18 +530,24 @@ igt_main
 		{ "chain-forked", CHAIN | FORKED },
 		{ "chain-interruptible", CHAIN | INTERRUPTIBLE },
 		{ "chain-sync", CHAIN | SYNC },
-		{ "contexts", CONTEXTS },
-		{ "contexts-interruptible", CONTEXTS | INTERRUPTIBLE},
-		{ "contexts-forked", CONTEXTS | FORKED},
-		{ "contexts-priority", CONTEXTS | FORKED | PRIORITY },
-		{ "contexts-chain", CONTEXTS | CHAIN },
-		{ "contexts-sync", CONTEXTS | SYNC },
 		{ "fds", FDS },
 		{ "fds-interruptible", FDS | INTERRUPTIBLE},
 		{ "fds-forked", FDS | FORKED},
 		{ "fds-priority", FDS | FORKED | PRIORITY },
 		{ "fds-chain", FDS | CHAIN},
 		{ "fds-sync", FDS | SYNC},
+		{ "contexts", CONTEXTS },
+		{ "contexts-interruptible", CONTEXTS | INTERRUPTIBLE},
+		{ "contexts-forked", CONTEXTS | FORKED},
+		{ "contexts-priority", CONTEXTS | FORKED | PRIORITY },
+		{ "contexts-chain", CONTEXTS | CHAIN },
+		{ "contexts-sync", CONTEXTS | SYNC },
+		{ "queues", QUEUES },
+		{ "queues-interruptible", QUEUES | INTERRUPTIBLE},
+		{ "queues-forked", QUEUES | FORKED},
+		{ "queues-priority", QUEUES | FORKED | PRIORITY },
+		{ "queues-chain", QUEUES | CHAIN },
+		{ "queues-sync", QUEUES | SYNC },
 		{ NULL }
 	};
 	int fd;
diff --git a/tests/meson.build b/tests/meson.build
index 9015f809e..017df8718 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -112,6 +112,7 @@ i915_progs = [
 	'gem_ctx_exec',
 	'gem_ctx_isolation',
 	'gem_ctx_param',
+	'gem_ctx_shared',
 	'gem_ctx_switch',
 	'gem_ctx_thrash',
 	'gem_double_irq_loop',
-- 
2.20.1

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

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

* [PATCH i-g-t 14/19] igt/gem_ctx_switch: Exercise queues
  2019-03-08 18:11 [PATCH i-g-t 01/19] i915/gem_ppgtt: Estimate resource usage and bail if it means swapping! Chris Wilson
                   ` (11 preceding siblings ...)
  2019-03-08 18:11 ` [PATCH i-g-t 13/19] i915: Exercise creating context with shared GTT Chris Wilson
@ 2019-03-08 18:11 ` Chris Wilson
  2019-03-08 18:11 ` [PATCH i-g-t 15/19] igt/gem_exec_whisper: Fork all-engine tests one-per-engine Chris Wilson
                   ` (6 subsequent siblings)
  19 siblings, 0 replies; 28+ messages in thread
From: Chris Wilson @ 2019-03-08 18:11 UTC (permalink / raw)
  To: intel-gfx

Queues are a form of contexts that share vm and enfore a single timeline
across all engines. Test switching between them, just like ordinary
contexts.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 tests/i915/gem_ctx_switch.c | 75 +++++++++++++++++++++++++++----------
 1 file changed, 55 insertions(+), 20 deletions(-)

diff --git a/tests/i915/gem_ctx_switch.c b/tests/i915/gem_ctx_switch.c
index 87e13b915..647911d4c 100644
--- a/tests/i915/gem_ctx_switch.c
+++ b/tests/i915/gem_ctx_switch.c
@@ -44,7 +44,8 @@
 #define LOCAL_I915_EXEC_NO_RELOC (1<<11)
 #define LOCAL_I915_EXEC_HANDLE_LUT (1<<12)
 
-#define INTERRUPTIBLE 1
+#define INTERRUPTIBLE 0x1
+#define QUEUE 0x2
 
 static double elapsed(const struct timespec *start, const struct timespec *end)
 {
@@ -126,8 +127,12 @@ static void single(int fd, uint32_t handle,
 
 	gem_require_ring(fd, e->exec_id | e->flags);
 
-	for (n = 0; n < 64; n++)
-		contexts[n] = gem_context_create(fd);
+	for (n = 0; n < 64; n++) {
+		if (flags & QUEUE)
+			contexts[n] = gem_queue_create(fd);
+		else
+			contexts[n] = gem_context_create(fd);
+	}
 
 	memset(&obj, 0, sizeof(obj));
 	obj.handle = handle;
@@ -232,8 +237,12 @@ static void all(int fd, uint32_t handle, unsigned flags, int timeout)
 	}
 	igt_require(nengine);
 
-	for (n = 0; n < ARRAY_SIZE(contexts); n++)
-		contexts[n] = gem_context_create(fd);
+	for (n = 0; n < ARRAY_SIZE(contexts); n++) {
+		if (flags & QUEUE)
+			contexts[n] = gem_queue_create(fd);
+		else
+			contexts[n] = gem_context_create(fd);
+	}
 
 	memset(obj, 0, sizeof(obj));
 	obj[1].handle = handle;
@@ -298,6 +307,17 @@ igt_main
 {
 	const int ncpus = sysconf(_SC_NPROCESSORS_ONLN);
 	const struct intel_execution_engine *e;
+	static const struct {
+		const char *name;
+		unsigned int flags;
+		bool (*require)(int fd);
+	} phases[] = {
+		{ "", 0, NULL },
+		{ "-interruptible", INTERRUPTIBLE, NULL },
+		{ "-queue", QUEUE, gem_has_queues },
+		{ "-queue-interruptible", QUEUE | INTERRUPTIBLE, gem_has_queues },
+		{ }
+	};
 	uint32_t light = 0, heavy;
 	int fd = -1;
 
@@ -319,21 +339,26 @@ igt_main
 	}
 
 	for (e = intel_execution_engines; e->name; e++) {
-		igt_subtest_f("%s%s", e->exec_id == 0 ? "basic-" : "", e->name)
-			single(fd, light, e, 0, 1, 5);
-
-		igt_skip_on_simulation();
-
-		igt_subtest_f("%s%s-heavy", e->exec_id == 0 ? "basic-" : "", e->name)
-			single(fd, heavy, e, 0, 1, 5);
-		igt_subtest_f("%s-interruptible", e->name)
-			single(fd, light, e, INTERRUPTIBLE, 1, 150);
-		igt_subtest_f("forked-%s", e->name)
-			single(fd, light, e, 0, ncpus, 150);
-		igt_subtest_f("forked-%s-heavy", e->name)
-			single(fd, heavy, e, 0, ncpus, 150);
-		igt_subtest_f("forked-%s-interruptible", e->name)
-			single(fd, light, e, INTERRUPTIBLE, ncpus, 150);
+		for (typeof(*phases) *p = phases; p->name; p++) {
+			igt_subtest_group {
+				igt_fixture {
+					if (p->require)
+						igt_require(p->require(fd));
+				}
+
+				igt_subtest_f("%s%s%s", e->exec_id == 0 ? "basic-" : "", e->name, p->name)
+					single(fd, light, e, p->flags, 1, 5);
+
+				igt_skip_on_simulation();
+
+				igt_subtest_f("%s%s-heavy%s", e->exec_id == 0 ? "basic-" : "", e->name, p->name)
+					single(fd, heavy, e, p->flags, 1, 5);
+				igt_subtest_f("forked-%s%s", e->name, p->name)
+					single(fd, light, e, p->flags, ncpus, 150);
+				igt_subtest_f("forked-%s-heavy%s", e->name, p->name)
+					single(fd, heavy, e, p->flags, ncpus, 150);
+			}
+		}
 	}
 
 	igt_subtest("basic-all-light")
@@ -341,6 +366,16 @@ igt_main
 	igt_subtest("basic-all-heavy")
 		all(fd, heavy, 0, 5);
 
+	igt_subtest_group {
+		igt_fixture {
+			igt_require(gem_has_queues(fd));
+		}
+		igt_subtest("basic-queue-light")
+			all(fd, light, QUEUE, 5);
+		igt_subtest("basic-queue-heavy")
+			all(fd, heavy, QUEUE, 5);
+	}
+
 	igt_fixture {
 		igt_stop_hang_detector();
 		gem_close(fd, heavy);
-- 
2.20.1

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

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

* [PATCH i-g-t 15/19] igt/gem_exec_whisper: Fork all-engine tests one-per-engine
  2019-03-08 18:11 [PATCH i-g-t 01/19] i915/gem_ppgtt: Estimate resource usage and bail if it means swapping! Chris Wilson
                   ` (12 preceding siblings ...)
  2019-03-08 18:11 ` [PATCH i-g-t 14/19] igt/gem_ctx_switch: Exercise queues Chris Wilson
@ 2019-03-08 18:11 ` Chris Wilson
  2019-03-08 18:11 ` [PATCH i-g-t 16/19] i915/gem_exec_whisper: debugfs/next_seqno is defunct Chris Wilson
                   ` (5 subsequent siblings)
  19 siblings, 0 replies; 28+ messages in thread
From: Chris Wilson @ 2019-03-08 18:11 UTC (permalink / raw)
  To: intel-gfx

Add a new mode for some more stress, submit the all-engines tests
simultaneously, a stream per engine.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 tests/i915/gem_exec_whisper.c | 27 ++++++++++++++++++++++-----
 1 file changed, 22 insertions(+), 5 deletions(-)

diff --git a/tests/i915/gem_exec_whisper.c b/tests/i915/gem_exec_whisper.c
index d3e0b0ba2..d5afc8119 100644
--- a/tests/i915/gem_exec_whisper.c
+++ b/tests/i915/gem_exec_whisper.c
@@ -88,6 +88,7 @@ static void verify_reloc(int fd, uint32_t handle,
 #define SYNC 0x40
 #define PRIORITY 0x80
 #define QUEUES 0x100
+#define ALL 0x200
 
 struct hang {
 	struct drm_i915_gem_exec_object2 obj;
@@ -199,6 +200,7 @@ static void whisper(int fd, unsigned engine, unsigned flags)
 	uint64_t old_offset;
 	int i, n, loc;
 	int debugfs;
+	int nchild;
 
 	if (flags & PRIORITY) {
 		igt_require(gem_scheduler_enabled(fd));
@@ -215,6 +217,7 @@ static void whisper(int fd, unsigned engine, unsigned flags)
 				engines[nengine++] = engine;
 		}
 	} else {
+		igt_assert(!(flags & ALL));
 		igt_require(gem_has_ring(fd, engine));
 		igt_require(gem_can_store_dword(fd, engine));
 		engines[nengine++] = engine;
@@ -233,11 +236,22 @@ static void whisper(int fd, unsigned engine, unsigned flags)
 	if (flags & HANG)
 		init_hang(&hang);
 
+	nchild = 1;
+	if (flags & FORKED)
+		nchild *= sysconf(_SC_NPROCESSORS_ONLN);
+	if (flags & ALL)
+		nchild *= nengine;
+
 	intel_detect_and_clear_missed_interrupts(fd);
 	gpu_power_read(&power, &sample[0]);
-	igt_fork(child, flags & FORKED ? sysconf(_SC_NPROCESSORS_ONLN) : 1)  {
+	igt_fork(child, nchild) {
 		unsigned int pass;
 
+		if (flags & ALL) {
+			engines[0] = engines[child % nengine];
+			nengine = 1;
+		}
+
 		memset(&scratch, 0, sizeof(scratch));
 		scratch.handle = gem_create(fd, 4096);
 		scratch.flags = EXEC_OBJECT_WRITE;
@@ -341,7 +355,7 @@ static void whisper(int fd, unsigned engine, unsigned flags)
 			igt_until_timeout(150) {
 				uint64_t offset;
 
-				if (!(flags & FORKED))
+				if (nchild == 1)
 					write_seqno(debugfs, pass);
 
 				if (flags & HANG)
@@ -382,8 +396,8 @@ static void whisper(int fd, unsigned engine, unsigned flags)
 
 				gem_write(fd, batches[1023].handle, loc, &pass, sizeof(pass));
 				for (n = 1024; --n >= 1; ) {
+					uint32_t handle[2] = {};
 					int this_fd = fd;
-					uint32_t handle[2];
 
 					execbuf.buffers_ptr = to_user_pointer(&batches[n-1]);
 					reloc_migrations += batches[n-1].offset != inter[n].presumed_offset;
@@ -550,7 +564,7 @@ igt_main
 		{ "queues-sync", QUEUES | SYNC },
 		{ NULL }
 	};
-	int fd;
+	int fd = -1;
 
 	igt_fixture {
 		fd = drm_open_driver_master(DRIVER_INTEL);
@@ -561,9 +575,12 @@ igt_main
 		igt_fork_hang_detector(fd);
 	}
 
-	for (const struct mode *m = modes; m->name; m++)
+	for (const struct mode *m = modes; m->name; m++) {
 		igt_subtest_f("%s", m->name)
 			whisper(fd, ALL_ENGINES, m->flags);
+		igt_subtest_f("%s-all", m->name)
+			whisper(fd, ALL_ENGINES, m->flags | ALL);
+	}
 
 	for (const struct intel_execution_engine *e = intel_execution_engines;
 	     e->name; e++) {
-- 
2.20.1

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

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

* [PATCH i-g-t 16/19] i915/gem_exec_whisper: debugfs/next_seqno is defunct
  2019-03-08 18:11 [PATCH i-g-t 01/19] i915/gem_ppgtt: Estimate resource usage and bail if it means swapping! Chris Wilson
                   ` (13 preceding siblings ...)
  2019-03-08 18:11 ` [PATCH i-g-t 15/19] igt/gem_exec_whisper: Fork all-engine tests one-per-engine Chris Wilson
@ 2019-03-08 18:11 ` Chris Wilson
  2019-03-08 18:11 ` [PATCH i-g-t 17/19] i915: Add gem_ctx_engines Chris Wilson
                   ` (4 subsequent siblings)
  19 siblings, 0 replies; 28+ messages in thread
From: Chris Wilson @ 2019-03-08 18:11 UTC (permalink / raw)
  To: intel-gfx

We removed next_seqno in 5.1, so time to wave goodbye.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 tests/i915/gem_exec_whisper.c | 12 ------------
 1 file changed, 12 deletions(-)

diff --git a/tests/i915/gem_exec_whisper.c b/tests/i915/gem_exec_whisper.c
index d5afc8119..61b8d6dac 100644
--- a/tests/i915/gem_exec_whisper.c
+++ b/tests/i915/gem_exec_whisper.c
@@ -44,15 +44,6 @@
 
 #define VERIFY 0
 
-static void write_seqno(int dir, unsigned offset)
-{
-	uint32_t seqno = UINT32_MAX - offset;
-
-	igt_sysfs_printf(dir, "i915_next_seqno", "0x%x", seqno);
-
-	igt_debug("next seqno set to: 0x%x\n", seqno);
-}
-
 static void check_bo(int fd, uint32_t handle, int pass)
 {
 	uint32_t *map;
@@ -355,9 +346,6 @@ static void whisper(int fd, unsigned engine, unsigned flags)
 			igt_until_timeout(150) {
 				uint64_t offset;
 
-				if (nchild == 1)
-					write_seqno(debugfs, pass);
-
 				if (flags & HANG)
 					submit_hang(&hang, engines, nengine, flags);
 
-- 
2.20.1

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

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

* [PATCH i-g-t 17/19] i915: Add gem_ctx_engines
  2019-03-08 18:11 [PATCH i-g-t 01/19] i915/gem_ppgtt: Estimate resource usage and bail if it means swapping! Chris Wilson
                   ` (14 preceding siblings ...)
  2019-03-08 18:11 ` [PATCH i-g-t 16/19] i915/gem_exec_whisper: debugfs/next_seqno is defunct Chris Wilson
@ 2019-03-08 18:11 ` Chris Wilson
  2019-03-11 10:00   ` Andi Shyti
  2019-03-08 18:11 ` [PATCH i-g-t 18/19] i915: Add gem_exec_balancer Chris Wilson
                   ` (3 subsequent siblings)
  19 siblings, 1 reply; 28+ messages in thread
From: Chris Wilson @ 2019-03-08 18:11 UTC (permalink / raw)
  To: intel-gfx

To exercise the new I915_CONTEXT_PARAM_ENGINES and interactions with
gem_execbuf().

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Tvrtko Ursulin <tvrtko.ursulin@linux.intel.com>
Cc: Andi Shyti <andi@etezian.org>
---
 tests/Makefile.sources       |   1 +
 tests/i915/gem_ctx_engines.c | 368 +++++++++++++++++++++++++++++++++++
 tests/meson.build            |   1 +
 3 files changed, 370 insertions(+)
 create mode 100644 tests/i915/gem_ctx_engines.c

diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index 9e0dab02e..41e756f15 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -21,6 +21,7 @@ TESTS_progs = \
 	drm_import_export \
 	drm_mm \
 	drm_read \
+	i915/gem_ctx_engines \
 	i915/gem_ctx_shared \
 	kms_3d \
 	kms_addfb_basic \
diff --git a/tests/i915/gem_ctx_engines.c b/tests/i915/gem_ctx_engines.c
new file mode 100644
index 000000000..acfdafa9c
--- /dev/null
+++ b/tests/i915/gem_ctx_engines.c
@@ -0,0 +1,368 @@
+/*
+ * Copyright © 2018 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ */
+
+#include "igt.h"
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+
+#include <drm.h>
+
+#include "i915/gem_context.h"
+
+#define engine_class(e, n) ((e)->class_instance[(n)].engine_class)
+#define engine_instance(e, n) ((e)->class_instance[(n)].engine_instance)
+
+static bool has_context_engines(int i915)
+{
+	struct drm_i915_gem_context_param param = {
+		.ctx_id = 0,
+		.param = I915_CONTEXT_PARAM_ENGINES,
+	};
+	return __gem_context_set_param(i915, &param) == 0;
+}
+
+static void invalid_engines(int i915)
+{
+	struct i915_context_param_engines stack = {}, *engines;
+	struct drm_i915_gem_context_param param = {
+		.ctx_id = gem_context_create(i915),
+		.param = I915_CONTEXT_PARAM_ENGINES,
+		.value = to_user_pointer(&stack),
+	};
+	uint32_t handle;
+	void *ptr;
+
+	param.size = 0;
+	igt_assert_eq(__gem_context_set_param(i915, &param), 0);
+
+	param.size = 1;
+	igt_assert_eq(__gem_context_set_param(i915, &param), -EINVAL);
+
+	param.size = sizeof(stack) - 1;
+	igt_assert_eq(__gem_context_set_param(i915, &param), -EINVAL);
+
+	param.size = sizeof(stack) + 1;
+	igt_assert_eq(__gem_context_set_param(i915, &param), -EINVAL);
+
+	param.size = 0;
+	igt_assert_eq(__gem_context_set_param(i915, &param), 0);
+
+	/* Create a single page surrounded by inaccessible nothingness */
+	ptr = mmap(NULL, 3 * 4096, PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
+	igt_assert(ptr != MAP_FAILED);
+
+	munmap(ptr, 4096);
+	engines = ptr + 4096;
+	munmap(ptr + 2 *4096, 4096);
+
+	param.size = sizeof(*engines) + sizeof(*engines->class_instance);
+	param.value = to_user_pointer(engines);
+
+	engines->class_instance[0].engine_class = -1;
+	igt_assert_eq(__gem_context_set_param(i915, &param), -ENOENT);
+
+	mprotect(engines, 4096, PROT_READ);
+	igt_assert_eq(__gem_context_set_param(i915, &param), -ENOENT);
+
+	mprotect(engines, 4096, PROT_WRITE);
+	engines->class_instance[0].engine_class = 0;
+	if (__gem_context_set_param(i915, &param)) /* XXX needs RCS */
+		goto out;
+
+	engines->extensions = to_user_pointer(ptr);
+	igt_assert_eq(__gem_context_set_param(i915, &param), -EFAULT);
+
+	engines->extensions = 0;
+	igt_assert_eq(__gem_context_set_param(i915, &param), 0);
+
+	param.value = to_user_pointer(engines - 1);
+	igt_assert_eq(__gem_context_set_param(i915, &param), -EFAULT);
+
+	param.value = to_user_pointer(engines) - 1;
+	igt_assert_eq(__gem_context_set_param(i915, &param), -EFAULT);
+
+	param.value = to_user_pointer(engines) - param.size +  1;
+	igt_assert_eq(__gem_context_set_param(i915, &param), -EFAULT);
+
+	param.value = to_user_pointer(engines) + 4096;
+	igt_assert_eq(__gem_context_set_param(i915, &param), -EFAULT);
+
+	param.value = to_user_pointer(engines) - param.size + 4096;
+	igt_assert_eq(__gem_context_set_param(i915, &param), 0);
+
+	param.value = to_user_pointer(engines) - param.size + 4096 + 1;
+	igt_assert_eq(__gem_context_set_param(i915, &param), -EFAULT);
+
+	param.value = to_user_pointer(engines) + 4096;
+	igt_assert_eq(__gem_context_set_param(i915, &param), -EFAULT);
+
+	param.value = to_user_pointer(engines) + 4096 - 1;
+	igt_assert_eq(__gem_context_set_param(i915, &param), -EFAULT);
+
+	param.value = to_user_pointer(engines) - 1;
+	igt_assert_eq(__gem_context_set_param(i915, &param), -EFAULT);
+
+	param.value = to_user_pointer(engines - 1);
+	igt_assert_eq(__gem_context_set_param(i915, &param), -EFAULT);
+
+	param.value = to_user_pointer(engines - 1) + 4096;
+	igt_assert_eq(__gem_context_set_param(i915, &param), -EFAULT);
+
+	param.value = to_user_pointer(engines - 1) + 4096 - sizeof(*engines->class_instance) / 2;
+	igt_assert_eq(__gem_context_set_param(i915, &param), -EFAULT);
+
+	handle = gem_create(i915, 4096 * 3);
+	ptr = gem_mmap__gtt(i915, handle, 4096 * 3, PROT_READ);
+	gem_close(i915, handle);
+
+	munmap(ptr, 4096);
+	munmap(ptr + 8192, 4096);
+
+	param.value = to_user_pointer(ptr + 4096);
+	igt_assert_eq(__gem_context_set_param(i915, &param), 0);
+
+	param.value = to_user_pointer(ptr);
+	igt_assert_eq(__gem_context_set_param(i915, &param), -EFAULT);
+
+	param.value = to_user_pointer(ptr) + 4095;
+	igt_assert_eq(__gem_context_set_param(i915, &param), -EFAULT);
+
+	param.value = to_user_pointer(ptr) + 8192;
+	igt_assert_eq(__gem_context_set_param(i915, &param), -EFAULT);
+
+	param.value = to_user_pointer(ptr) + 12287;
+	igt_assert_eq(__gem_context_set_param(i915, &param), -EFAULT);
+
+	munmap(ptr + 4096, 4096);
+
+out:
+	munmap(engines, 4096);
+	gem_context_destroy(i915, param.ctx_id);
+}
+
+static void execute_one(int i915)
+{
+	I915_DEFINE_CONTEXT_PARAM_ENGINES(engines , I915_EXEC_RING_MASK + 1);
+	struct drm_i915_gem_context_param param = {
+		.ctx_id = gem_context_create(i915),
+		.param = I915_CONTEXT_PARAM_ENGINES,
+		.value = to_user_pointer(&engines),
+		/* .size to be filled in later */
+	};
+	struct drm_i915_gem_exec_object2 obj = {
+		.handle = gem_create(i915, 4096),
+	};
+	struct drm_i915_gem_execbuffer2 execbuf = {
+		.buffers_ptr = to_user_pointer(&obj),
+		.buffer_count = 1,
+		.rsvd1 = param.ctx_id,
+	};
+	const uint32_t bbe = MI_BATCH_BUFFER_END;
+	const struct intel_execution_engine2 *e;
+
+	gem_write(i915, obj.handle, 0, &bbe, sizeof(bbe));
+
+	/* Unadulterated I915_EXEC_DEFAULT should work */
+	execbuf.flags = 0;
+	igt_assert_eq(__gem_execbuf(i915, &execbuf), 0);
+	gem_sync(i915, obj.handle);
+
+	for_each_engine_class_instance(i915, e) {
+		struct drm_i915_gem_busy busy = { .handle = obj.handle };
+
+		for (int i = -1; i <= I915_EXEC_RING_MASK; i++) {
+			igt_spin_t *spin;
+
+			memset(&engines, 0, sizeof(engines));
+			engine_class(&engines, 0) = e->class;
+			engine_instance(&engines, 0) = e->instance;
+			param.size = offsetof(typeof(engines), class_instance[1]);
+			gem_context_set_param(i915, &param);
+
+			spin = igt_spin_batch_new(i915,
+						  .ctx = param.ctx_id,
+						  .engine = 0);
+
+			igt_debug("Testing with map of %d engines\n", i + 1);
+			memset(&engines.class_instance, -1, sizeof(engines.class_instance));
+			if (i != -1) {
+				engine_class(&engines, i) = e->class;
+				engine_instance(&engines, i) = e->instance;
+			}
+			param.size = sizeof(uint64_t) + (i + 1) * sizeof(uint32_t);
+			gem_context_set_param(i915, &param);
+
+			for (int j = 0; j <= I915_EXEC_RING_MASK; j++) {
+				int err;
+
+				execbuf.flags = j;
+				err =__gem_execbuf(i915, &execbuf);
+				if (j == i) {
+					igt_assert_f(err == 0,
+						     "Failed to report the valid engine for slot %d\n",
+						     i);
+				} else {
+					igt_assert_f(err == -EINVAL,
+						     "Failed to report an invalid engine for slot %d (valid at %d)\n",
+						     j, i);
+				}
+			}
+
+			do_ioctl(i915, DRM_IOCTL_I915_GEM_BUSY, &busy);
+			if (i != -1) {
+				igt_assert_eq(busy.busy, 1 << (e->class + 16));
+			} else {
+				igt_assert_eq(busy.busy, 0);
+			}
+
+			igt_spin_batch_free(i915, spin);
+
+			gem_sync(i915, obj.handle);
+			do_ioctl(i915, DRM_IOCTL_I915_GEM_BUSY, &busy);
+			igt_assert_eq(busy.busy, 0);
+		}
+	}
+
+	/* Restore the defaults and check I915_EXEC_DEFAULT works again. */
+	param.size = 0;
+	gem_context_set_param(i915, &param);
+	execbuf.flags = 0;
+	igt_assert_eq(__gem_execbuf(i915, &execbuf), 0);
+
+	gem_close(i915, obj.handle);
+	gem_context_destroy(i915, param.ctx_id);
+}
+
+static void execute_oneforall(int i915)
+{
+	I915_DEFINE_CONTEXT_PARAM_ENGINES(engines , I915_EXEC_RING_MASK + 1);
+	struct drm_i915_gem_context_param param = {
+		.ctx_id = gem_context_create(i915),
+		.param = I915_CONTEXT_PARAM_ENGINES,
+		.value = to_user_pointer(&engines),
+		.size = sizeof(engines),
+	};
+	const struct intel_execution_engine2 *e;
+
+	for_each_engine_class_instance(i915, e) {
+		memset(&engines, 0, sizeof(engines));
+		for (int i = 0; i <= I915_EXEC_RING_MASK; i++) {
+			engine_class(&engines, i) = e->class;
+			engine_instance(&engines, i) = e->instance;
+		}
+		gem_context_set_param(i915, &param);
+
+		for (int i = 0; i <= I915_EXEC_RING_MASK; i++) {
+			struct drm_i915_gem_busy busy = {};
+			igt_spin_t *spin;
+
+			spin = __igt_spin_batch_new(i915,
+						    .ctx = param.ctx_id,
+						    .engine = i);
+
+			busy.handle = spin->handle;
+			do_ioctl(i915, DRM_IOCTL_I915_GEM_BUSY, &busy);
+			igt_assert_eq(busy.busy, 1 << (e->class + 16));
+
+			igt_spin_batch_free(i915, spin);
+		}
+	}
+
+	gem_context_destroy(i915, param.ctx_id);
+}
+
+static void execute_allforone(int i915)
+{
+	I915_DEFINE_CONTEXT_PARAM_ENGINES(engines , I915_EXEC_RING_MASK + 1);
+	struct drm_i915_gem_context_param param = {
+		.ctx_id = gem_context_create(i915),
+		.param = I915_CONTEXT_PARAM_ENGINES,
+		.value = to_user_pointer(&engines),
+	};
+	const struct intel_execution_engine2 *e;
+	int i;
+
+	i = 0;
+	memset(&engines, 0, sizeof(engines));
+	for_each_engine_class_instance(i915, e) {
+		engine_class(&engines, i) = e->class;
+		engine_instance(&engines, i) = e->instance;
+		i++;
+	}
+	param.size = sizeof(uint64_t) + i * sizeof(uint32_t);
+	gem_context_set_param(i915, &param);
+
+	i = 0;
+	for_each_engine_class_instance(i915, e) {
+		struct drm_i915_gem_busy busy = {};
+		igt_spin_t *spin;
+
+		spin = __igt_spin_batch_new(i915,
+					    .ctx = param.ctx_id,
+					    .engine = i++);
+
+		busy.handle = spin->handle;
+		do_ioctl(i915, DRM_IOCTL_I915_GEM_BUSY, &busy);
+		igt_assert_eq(busy.busy, 1 << (e->class + 16));
+
+		igt_spin_batch_free(i915, spin);
+	}
+
+	gem_context_destroy(i915, param.ctx_id);
+}
+
+igt_main
+{
+	int i915 = -1;
+
+	igt_fixture {
+		i915 = drm_open_driver_render(DRIVER_INTEL);
+		igt_require_gem(i915);
+
+		gem_require_contexts(i915);
+		igt_require(has_context_engines(i915));
+	}
+
+	igt_subtest("invalid-engines")
+		invalid_engines(i915);
+
+	igt_subtest("execute-one")
+		execute_one(i915);
+
+	igt_subtest("execute-oneforall")
+		execute_oneforall(i915);
+
+	igt_subtest("execute-allforone")
+		execute_allforone(i915);
+}
diff --git a/tests/meson.build b/tests/meson.build
index 017df8718..0539c20c8 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -109,6 +109,7 @@ i915_progs = [
 	'gem_cs_tlb',
 	'gem_ctx_bad_destroy',
 	'gem_ctx_create',
+	'gem_ctx_engines',
 	'gem_ctx_exec',
 	'gem_ctx_isolation',
 	'gem_ctx_param',
-- 
2.20.1

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

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

* [PATCH i-g-t 18/19] i915: Add gem_exec_balancer
  2019-03-08 18:11 [PATCH i-g-t 01/19] i915/gem_ppgtt: Estimate resource usage and bail if it means swapping! Chris Wilson
                   ` (15 preceding siblings ...)
  2019-03-08 18:11 ` [PATCH i-g-t 17/19] i915: Add gem_ctx_engines Chris Wilson
@ 2019-03-08 18:11 ` Chris Wilson
  2019-03-12 10:23   ` Tvrtko Ursulin
  2019-03-08 18:11 ` [PATCH i-g-t 19/19] i915/gem_exec_balancer: Exercise bonded pairs Chris Wilson
                   ` (2 subsequent siblings)
  19 siblings, 1 reply; 28+ messages in thread
From: Chris Wilson @ 2019-03-08 18:11 UTC (permalink / raw)
  To: intel-gfx

Exercise the in-kernel load balancer checking that we can distribute
batches across the set of ctx->engines to avoid load.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 tests/Makefile.am              |   1 +
 tests/Makefile.sources         |   1 +
 tests/i915/gem_exec_balancer.c | 627 +++++++++++++++++++++++++++++++++
 tests/meson.build              |   7 +
 4 files changed, 636 insertions(+)
 create mode 100644 tests/i915/gem_exec_balancer.c

diff --git a/tests/Makefile.am b/tests/Makefile.am
index 289249b42..68a9c14bf 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -102,6 +102,7 @@ gem_close_race_LDADD = $(LDADD) -lpthread
 gem_ctx_thrash_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_ctx_thrash_LDADD = $(LDADD) -lpthread
 gem_ctx_sseu_LDADD = $(LDADD) $(top_builddir)/lib/libigt_perf.la
+i915_gem_exec_balancer_LDADD = $(LDADD) $(top_builddir)/lib/libigt_perf.la
 gem_exec_capture_LDADD = $(LDADD) -lz
 gem_exec_parallel_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_exec_parallel_LDADD = $(LDADD) -lpthread
diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index 41e756f15..f6c21a1aa 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -23,6 +23,7 @@ TESTS_progs = \
 	drm_read \
 	i915/gem_ctx_engines \
 	i915/gem_ctx_shared \
+	i915/gem_exec_balancer \
 	kms_3d \
 	kms_addfb_basic \
 	kms_atomic \
diff --git a/tests/i915/gem_exec_balancer.c b/tests/i915/gem_exec_balancer.c
new file mode 100644
index 000000000..d9fdffe67
--- /dev/null
+++ b/tests/i915/gem_exec_balancer.c
@@ -0,0 +1,627 @@
+/*
+ * Copyright © 2018 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <sched.h>
+
+#include "igt.h"
+#include "igt_perf.h"
+#include "i915/gem_ring.h"
+#include "sw_sync.h"
+
+IGT_TEST_DESCRIPTION("Exercise in-kernel load-balancing");
+
+struct class_instance {
+	uint16_t class;
+	uint16_t instance;
+};
+#define INSTANCE_COUNT (1 << I915_PMU_SAMPLE_INSTANCE_BITS)
+
+static bool has_class_instance(int i915, uint16_t class, uint16_t instance)
+{
+	int fd;
+
+	fd = perf_i915_open(I915_PMU_ENGINE_BUSY(class, instance));
+	if (fd != -1) {
+		close(fd);
+		return true;
+	}
+
+	return false;
+}
+
+static struct class_instance *
+list_engines(int i915, uint32_t class_mask, unsigned int *out)
+{
+	unsigned int count = 0, size = 64;
+	struct class_instance *engines;
+
+	engines = malloc(size * sizeof(*engines));
+	if (!engines) {
+		*out = 0;
+		return NULL;
+	}
+
+	for (enum drm_i915_gem_engine_class class = I915_ENGINE_CLASS_RENDER;
+	     class_mask;
+	     class++, class_mask >>= 1) {
+		if (!(class_mask & 1))
+			continue;
+
+		for (unsigned int instance = 0;
+		     instance < INSTANCE_COUNT;
+		     instance++) {
+		     if (!has_class_instance(i915, class, instance))
+			     continue;
+
+			if (count == size) {
+				struct class_instance *e;
+
+				size *= 2;
+				e = realloc(engines, size*sizeof(*engines));
+				if (!e) {
+					*out = count;
+					return engines;
+				}
+
+				engines = e;
+			}
+
+			engines[count++] = (struct class_instance){
+				.class = class,
+				.instance = instance,
+			};
+		}
+	}
+
+	if (!count) {
+		free(engines);
+		engines = NULL;
+	}
+
+	*out = count;
+	return engines;
+}
+
+static int __set_load_balancer(int i915, uint32_t ctx,
+			       const struct class_instance *ci,
+			       unsigned int count)
+{
+	struct i915_context_engines_load_balance balancer = {
+		{ .name = I915_CONTEXT_ENGINES_EXT_LOAD_BALANCE },
+		.engines_mask = ~0ull,
+	};
+	I915_DEFINE_CONTEXT_PARAM_ENGINES(engines, count + 1);
+	struct drm_i915_gem_context_param p = {
+		.ctx_id = ctx,
+		.param = I915_CONTEXT_PARAM_ENGINES,
+		.size = sizeof(&engines),
+		.value = to_user_pointer(&engines)
+	};
+
+	engines.extensions = to_user_pointer(&balancer),
+	engines.class_instance[0].engine_class = I915_ENGINE_CLASS_INVALID;
+	engines.class_instance[0].engine_instance = I915_ENGINE_CLASS_INVALID_NONE;
+	memcpy(engines.class_instance + 1, ci, count * sizeof(uint32_t));
+
+	return __gem_context_set_param(i915, &p);
+}
+
+static void set_load_balancer(int i915, uint32_t ctx,
+			      const struct class_instance *ci,
+			      unsigned int count)
+{
+	igt_assert_eq(__set_load_balancer(i915, ctx, ci, count), 0);
+}
+
+static uint32_t load_balancer_create(int i915,
+				     const struct class_instance *ci,
+				     unsigned int count)
+{
+	uint32_t ctx;
+
+	ctx = gem_context_create(i915);
+	set_load_balancer(i915, ctx, ci, count);
+
+	return ctx;
+}
+
+static void kick_kthreads(int period_us)
+{
+	sched_yield();
+	usleep(period_us);
+}
+
+static double measure_load(int pmu, int period_us)
+{
+	uint64_t data[2];
+	uint64_t d_t, d_v;
+
+	kick_kthreads(period_us);
+
+	igt_assert_eq(read(pmu, data, sizeof(data)), sizeof(data));
+	d_v = -data[0];
+	d_t = -data[1];
+
+	usleep(period_us);
+
+	igt_assert_eq(read(pmu, data, sizeof(data)), sizeof(data));
+	d_v += data[0];
+	d_t += data[1];
+
+	return d_v / (double)d_t;
+}
+
+static double measure_min_load(int pmu, unsigned int num, int period_us)
+{
+	uint64_t data[2 + num];
+	uint64_t d_t, d_v[num];
+	uint64_t min = -1, max = 0;
+
+	kick_kthreads(period_us);
+
+	igt_assert_eq(read(pmu, data, sizeof(data)), sizeof(data));
+	for (unsigned int n = 0; n < num; n++)
+		d_v[n] = -data[2 + n];
+	d_t = -data[1];
+
+	usleep(period_us);
+
+	igt_assert_eq(read(pmu, data, sizeof(data)), sizeof(data));
+
+	d_t += data[1];
+	for (unsigned int n = 0; n < num; n++) {
+		d_v[n] += data[2 + n];
+		igt_debug("engine[%d]: %.1f%%\n",
+			  n, d_v[n] / (double)d_t * 100);
+		if (d_v[n] < min)
+			min = d_v[n];
+		if (d_v[n] > max)
+			max = d_v[n];
+	}
+
+	igt_debug("elapsed: %"PRIu64"ns, load [%.1f, %.1f]%%\n",
+		  d_t, min / (double)d_t * 100,  max / (double)d_t * 100);
+
+	return min / (double)d_t;
+}
+
+static void check_individual_engine(int i915,
+				    uint32_t ctx,
+				    const struct class_instance *ci,
+				    int idx)
+{
+	igt_spin_t *spin;
+	double load;
+	int pmu;
+
+	pmu = perf_i915_open(I915_PMU_ENGINE_BUSY(ci[idx].class,
+						  ci[idx].instance));
+
+	spin = igt_spin_batch_new(i915, .ctx = ctx, .engine = idx + 1);
+	load = measure_load(pmu, 10000);
+	igt_spin_batch_free(i915, spin);
+
+	close(pmu);
+
+	igt_assert_f(load > 0.90,
+		     "engine %d (class:instance %d:%d) was found to be only %.1f%% busy\n",
+		     idx, ci[idx].class, ci[idx].instance, load*100);
+}
+
+static void individual(int i915)
+{
+	uint32_t ctx;
+
+	/*
+	 * I915_CONTEXT_PARAM_ENGINE allows us to index into the user
+	 * supplied array from gem_execbuf(). Our check is to build the
+	 * ctx->engine[] with various different engine classes, feed in
+	 * a spinner and then ask pmu to confirm it the expected engine
+	 * was busy.
+	 */
+
+	ctx = gem_context_create(i915);
+
+	for (int mask = 0; mask < 32; mask++) {
+		struct class_instance *ci;
+		unsigned int count;
+
+		ci = list_engines(i915, 1u << mask, &count);
+		if (!ci)
+			continue;
+
+		igt_debug("Found %d engines of class %d\n", count, mask);
+
+		for (int pass = 0; pass < count; pass++) { /* approx. count! */
+			igt_permute_array(ci, count, igt_exchange_int64);
+			set_load_balancer(i915, ctx, ci, count);
+			for (unsigned int n = 0; n < count; n++)
+				check_individual_engine(i915, ctx, ci, n);
+		}
+
+		free(ci);
+	}
+
+	gem_context_destroy(i915, ctx);
+}
+
+static int add_pmu(int pmu, const struct class_instance *ci)
+{
+	return perf_i915_open_group(I915_PMU_ENGINE_BUSY(ci->class,
+							 ci->instance),
+				    pmu);
+}
+
+static uint32_t __batch_create(int i915, uint32_t offset)
+{
+	const uint32_t bbe = MI_BATCH_BUFFER_END;
+	uint32_t handle;
+
+	handle = gem_create(i915, ALIGN(offset + 4, 4096));
+	gem_write(i915, handle, offset, &bbe, sizeof(bbe));
+
+	return handle;
+}
+
+static uint32_t batch_create(int i915)
+{
+	return __batch_create(i915, 0);
+}
+
+static void full(int i915, unsigned int flags)
+#define PULSE 0x1
+#define LATE 0x2
+{
+	struct drm_i915_gem_exec_object2 batch = {
+		.handle = batch_create(i915),
+	};
+
+	if (flags & LATE)
+		igt_require_sw_sync();
+
+	/*
+	 * I915_CONTEXT_PARAM_ENGINE changes the meaning of I915_EXEC_DEFAULT
+	 * to provide an automatic selection from the ctx->engine[]. It
+	 * employs load-balancing to evenly distribute the workload the
+	 * array. If we submit N spinners, we expect them to be simultaneously
+	 * running across N engines and use PMU to confirm that the entire
+	 * set of engines are busy.
+	 *
+	 * We complicate matters by interpersing shortlived tasks to challenge
+	 * the kernel to search for space in which to insert new batches.
+	 */
+
+
+	for (int mask = 0; mask < 32; mask++) {
+		struct class_instance *ci;
+		igt_spin_t *spin = NULL;
+		unsigned int count;
+		IGT_CORK_FENCE(cork);
+		double load;
+		int fence = -1;
+		int *pmu;
+
+		ci = list_engines(i915, 1u << mask, &count);
+		if (!ci)
+			continue;
+
+		igt_debug("Found %d engines of class %d\n", count, mask);
+
+		pmu = malloc(sizeof(*pmu) * count);
+		igt_assert(pmu);
+
+		if (flags & LATE)
+			fence = igt_cork_plug(&cork, i915);
+
+		pmu[0] = -1;
+		for (unsigned int n = 0; n < count; n++) {
+			uint32_t ctx;
+
+			pmu[n] = add_pmu(pmu[0], &ci[n]);
+
+			if (flags & PULSE) {
+				struct drm_i915_gem_execbuffer2 eb = {
+					.buffers_ptr = to_user_pointer(&batch),
+					.buffer_count = 1,
+					.rsvd2 = fence,
+					.flags = flags & LATE ? I915_EXEC_FENCE_IN : 0,
+				};
+
+				gem_execbuf(i915, &eb);
+			}
+
+			/*
+			 * Each spinner needs to be one a new timeline,
+			 * otherwise they will just sit in the single queue
+			 * and not run concurrently.
+			 */
+			ctx = load_balancer_create(i915, ci, count);
+
+			if (spin == NULL) {
+				spin = __igt_spin_batch_new(i915, ctx, 0, 0);
+			} else {
+				struct drm_i915_gem_exec_object2 obj = {
+					.handle = spin->handle,
+				};
+				struct drm_i915_gem_execbuffer2 eb = {
+					.buffers_ptr = to_user_pointer(&obj),
+					.buffer_count = 1,
+					.rsvd1 = ctx,
+					.rsvd2 = fence,
+					.flags = flags & LATE ? I915_EXEC_FENCE_IN : 0,
+				};
+
+				gem_execbuf(i915, &eb);
+			}
+
+			gem_context_destroy(i915, ctx);
+		}
+
+		if (flags & LATE) {
+			igt_cork_unplug(&cork);
+			close(fence);
+		}
+
+		load = measure_min_load(pmu[0], count, 10000);
+		igt_spin_batch_free(i915, spin);
+
+		close(pmu[0]);
+		free(pmu);
+
+		free(ci);
+
+		igt_assert_f(load > 0.90,
+			     "minimum load for %d x class:%d was found to be only %.1f%% busy\n",
+			     count, mask, load*100);
+	}
+
+	gem_close(i915, batch.handle);
+}
+
+static void ping(int i915, uint32_t ctx, unsigned int engine)
+{
+	struct drm_i915_gem_exec_object2 obj = {
+		.handle = batch_create(i915),
+	};
+	struct drm_i915_gem_execbuffer2 execbuf = {
+		.buffers_ptr = to_user_pointer(&obj),
+		.buffer_count = 1,
+		.flags = engine,
+		.rsvd1 = ctx,
+	};
+	gem_execbuf(i915, &execbuf);
+	gem_sync(i915, obj.handle);
+	gem_close(i915, obj.handle);
+}
+
+static void semaphore(int i915)
+{
+	uint32_t block[2], scratch;
+	igt_spin_t *spin[3];
+
+	/*
+	 * If we are using HW semaphores to launch serialised requests
+	 * on different engine concurrently, we want to verify that real
+	 * work is unimpeded.
+	 */
+	igt_require(gem_scheduler_has_preemption(i915));
+
+	block[0] = gem_context_create(i915);
+	block[1] = gem_context_create(i915);
+
+	scratch = gem_create(i915, 4096);
+	spin[2] = igt_spin_batch_new(i915, .dependency = scratch);
+	for (int mask = 1; mask < 32; mask++) {
+		struct class_instance *ci;
+		unsigned int count;
+		uint32_t vip;
+
+		ci = list_engines(i915, 1u << mask, &count);
+		if (!ci)
+			continue;
+
+		if (count < ARRAY_SIZE(block))
+			continue;
+
+		/* Ensure that we completely occupy all engines in this group */
+		count = ARRAY_SIZE(block);
+
+		for (int i = 0; i < count; i++) {
+			set_load_balancer(i915, block[i], ci, count);
+			spin[i] = __igt_spin_batch_new(i915,
+						       .ctx = block[i],
+						       .dependency = scratch);
+		}
+
+		/*
+		 * Either we haven't blocked both engines with semaphores,
+		 * or we let the vip through. If not, we hang.
+		 */
+		vip = gem_context_create(i915);
+		set_load_balancer(i915, vip, ci, count);
+		ping(i915, vip, 0);
+		gem_context_destroy(i915, vip);
+
+		for (int i = 0; i < count; i++)
+			igt_spin_batch_free(i915, spin[i]);
+
+		free(ci);
+	}
+	igt_spin_batch_free(i915, spin[2]);
+	gem_close(i915, scratch);
+
+	gem_context_destroy(i915, block[1]);
+	gem_context_destroy(i915, block[0]);
+}
+
+static void smoketest(int i915, int timeout)
+{
+	struct drm_i915_gem_exec_object2 batch[2] = {
+		{ .handle = __batch_create(i915, 16380) }
+	};
+	unsigned int ncontext = 0;
+	uint32_t *contexts = NULL;
+	uint32_t *handles = NULL;
+
+	igt_require_sw_sync();
+
+	for (int mask = 0; mask < 32; mask++) {
+		struct class_instance *ci;
+		unsigned int count = 0;
+
+		ci = list_engines(i915, 1u << mask, &count);
+		if (!ci || count < 2) {
+			free(ci);
+			continue;
+		}
+
+		igt_debug("Found %d engines of class %d\n", count, mask);
+
+		ncontext += 128;
+		contexts = realloc(contexts, sizeof(*contexts) * ncontext);
+		igt_assert(contexts);
+
+		for (unsigned int n = ncontext - 128; n < ncontext; n++) {
+			contexts[n] = load_balancer_create(i915, ci, count);
+			igt_assert(contexts[n]);
+		}
+
+		free(ci);
+	}
+	igt_debug("Created %d virtual engines (one per context)\n", ncontext);
+	igt_require(ncontext);
+
+	contexts = realloc(contexts, sizeof(*contexts) * ncontext * 4);
+	igt_assert(contexts);
+	memcpy(contexts + ncontext, contexts, ncontext * sizeof(*contexts));
+	ncontext *= 2;
+	memcpy(contexts + ncontext, contexts, ncontext * sizeof(*contexts));
+	ncontext *= 2;
+
+	handles = malloc(sizeof(*handles) * ncontext);
+	igt_assert(handles);
+	for (unsigned int n = 0; n < ncontext; n++)
+		handles[n] = gem_create(i915, 4096);
+
+	igt_until_timeout(timeout) {
+		unsigned int count = 1 + (rand() % (ncontext - 1));
+		IGT_CORK_FENCE(cork);
+		int fence = igt_cork_plug(&cork, i915);
+
+		for (unsigned int n = 0; n < count; n++) {
+			struct drm_i915_gem_execbuffer2 eb = {
+				.buffers_ptr = to_user_pointer(batch),
+				.buffer_count = ARRAY_SIZE(batch),
+				.rsvd1 = contexts[n],
+				.rsvd2 = fence,
+				.flags = I915_EXEC_BATCH_FIRST | I915_EXEC_FENCE_IN,
+			};
+			batch[1].handle = handles[n];
+			gem_execbuf(i915, &eb);
+		}
+		igt_permute_array(handles, count, igt_exchange_int);
+
+		igt_cork_unplug(&cork);
+		for (unsigned int n = 0; n < count; n++)
+			gem_sync(i915, handles[n]);
+
+		close(fence);
+	}
+
+	for (unsigned int n = 0; n < ncontext; n++) {
+		gem_close(i915, handles[n]);
+		__gem_context_destroy(i915, contexts[n]);
+	}
+	free(handles);
+	free(contexts);
+	gem_close(i915, batch[0].handle);
+}
+
+static bool has_context_engines(int i915)
+{
+	struct drm_i915_gem_context_param p = {
+		.param = I915_CONTEXT_PARAM_ENGINES,
+	};
+
+	return __gem_context_set_param(i915, &p) == 0;
+}
+
+static bool has_load_balancer(int i915)
+{
+	struct class_instance ci = {};
+	uint32_t ctx;
+	int err;
+
+	ctx = gem_context_create(i915);
+	err = __set_load_balancer(i915, ctx, &ci, 1);
+	gem_context_destroy(i915, ctx);
+
+	return err == 0;
+}
+
+igt_main
+{
+	int i915 = -1;
+
+	igt_skip_on_simulation();
+
+	igt_fixture {
+		i915 = drm_open_driver(DRIVER_INTEL);
+		igt_require_gem(i915);
+
+		gem_require_contexts(i915);
+		igt_require(has_context_engines(i915));
+		igt_require(has_load_balancer(i915));
+
+		igt_fork_hang_detector(i915);
+	}
+
+	igt_subtest("individual")
+		individual(i915);
+
+	igt_subtest_group {
+		static const struct {
+			const char *name;
+			unsigned int flags;
+		} phases[] = {
+			{ "", 0 },
+			{ "-pulse", PULSE },
+			{ "-late", LATE },
+			{ "-late-pulse", PULSE | LATE },
+			{ }
+		};
+		for (typeof(*phases) *p = phases; p->name; p++)
+			igt_subtest_f("full%s", p->name)
+				full(i915, p->flags);
+	}
+
+	igt_subtest("semaphore")
+		semaphore(i915);
+
+	igt_subtest("smoke")
+		smoketest(i915, 20);
+
+	igt_fixture {
+		igt_stop_hang_detector();
+	}
+}
diff --git a/tests/meson.build b/tests/meson.build
index 0539c20c8..bd2db825e 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -293,6 +293,13 @@ test_executables += executable('gem_eio',
 	   install : true)
 test_list += 'gem_eio'
 
+test_executables += executable('gem_exec_balancer', 'i915/gem_exec_balancer.c',
+	   dependencies : test_deps + [ lib_igt_perf ],
+	   install_dir : libexecdir,
+	   install_rpath : libexecdir_rpathdir,
+	   install : true)
+test_progs += 'gem_exec_balancer'
+
 test_executables += executable('gem_mocs_settings',
 	   join_paths('i915', 'gem_mocs_settings.c'),
 	   dependencies : test_deps + [ lib_igt_perf ],
-- 
2.20.1

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

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

* [PATCH i-g-t 19/19] i915/gem_exec_balancer: Exercise bonded pairs
  2019-03-08 18:11 [PATCH i-g-t 01/19] i915/gem_ppgtt: Estimate resource usage and bail if it means swapping! Chris Wilson
                   ` (16 preceding siblings ...)
  2019-03-08 18:11 ` [PATCH i-g-t 18/19] i915: Add gem_exec_balancer Chris Wilson
@ 2019-03-08 18:11 ` Chris Wilson
  2019-03-11 10:18   ` Andi Shyti
  2019-03-11 10:30   ` Andi Shyti
  2019-03-09  2:07 ` ✗ Fi.CI.BAT: failure for series starting with [01/19] i915/gem_ppgtt: Estimate resource usage and bail if it means swapping! Patchwork
  2019-03-11 10:04 ` [PATCH i-g-t 01/19] " Mika Kuoppala
  19 siblings, 2 replies; 28+ messages in thread
From: Chris Wilson @ 2019-03-08 18:11 UTC (permalink / raw)
  To: intel-gfx

The submit-fence + load_balancing apis allow for us to execute a named
pair of engines in parallel; that this by submitting a request to one
engine, we can then use the generated submit-fence to submit a second
request to another engine and have it execute at the same time.
Furthermore, by specifying bonded pairs, we can direct the virtual
engine to use a particular engine in parallel to the first request.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 tests/i915/gem_exec_balancer.c | 174 +++++++++++++++++++++++++++++++--
 1 file changed, 165 insertions(+), 9 deletions(-)

diff --git a/tests/i915/gem_exec_balancer.c b/tests/i915/gem_exec_balancer.c
index d9fdffe67..e727090e4 100644
--- a/tests/i915/gem_exec_balancer.c
+++ b/tests/i915/gem_exec_balancer.c
@@ -102,12 +102,44 @@ list_engines(int i915, uint32_t class_mask, unsigned int *out)
 	return engines;
 }
 
+static int __set_engines(int i915, uint32_t ctx,
+			 const struct class_instance *ci,
+			 unsigned int count)
+{
+	struct engines {
+		uint64_t extension;
+		uint64_t class_instance[count];
+	} engines;
+	struct drm_i915_gem_context_param p = {
+		.ctx_id = ctx,
+		.param = I915_CONTEXT_PARAM_ENGINES,
+		.size = sizeof(engines),
+		.value = to_user_pointer(&engines)
+	};
+
+	engines.extension = 0;
+	memcpy(engines.class_instance, ci, sizeof(engines.class_instance));
+
+	return __gem_context_set_param(i915, &p);
+}
+
+static void set_engines(int i915, uint32_t ctx,
+			const struct class_instance *ci,
+			unsigned int count)
+{
+	igt_assert_eq(__set_engines(i915, ctx, ci, count), 0);
+}
+
 static int __set_load_balancer(int i915, uint32_t ctx,
 			       const struct class_instance *ci,
-			       unsigned int count)
+			       unsigned int count,
+			       void *ext)
 {
 	struct i915_context_engines_load_balance balancer = {
-		{ .name = I915_CONTEXT_ENGINES_EXT_LOAD_BALANCE },
+		{ 
+			.next_extension = to_user_pointer(ext),
+			.name = I915_CONTEXT_ENGINES_EXT_LOAD_BALANCE
+	       	},
 		.engines_mask = ~0ull,
 	};
 	I915_DEFINE_CONTEXT_PARAM_ENGINES(engines, count + 1);
@@ -128,9 +160,10 @@ static int __set_load_balancer(int i915, uint32_t ctx,
 
 static void set_load_balancer(int i915, uint32_t ctx,
 			      const struct class_instance *ci,
-			      unsigned int count)
+			      unsigned int count,
+			      void *ext)
 {
-	igt_assert_eq(__set_load_balancer(i915, ctx, ci, count), 0);
+	igt_assert_eq(__set_load_balancer(i915, ctx, ci, count, ext), 0);
 }
 
 static uint32_t load_balancer_create(int i915,
@@ -140,7 +173,7 @@ static uint32_t load_balancer_create(int i915,
 	uint32_t ctx;
 
 	ctx = gem_context_create(i915);
-	set_load_balancer(i915, ctx, ci, count);
+	set_load_balancer(i915, ctx, ci, count, NULL);
 
 	return ctx;
 }
@@ -254,7 +287,7 @@ static void individual(int i915)
 
 		for (int pass = 0; pass < count; pass++) { /* approx. count! */
 			igt_permute_array(ci, count, igt_exchange_int64);
-			set_load_balancer(i915, ctx, ci, count);
+			set_load_balancer(i915, ctx, ci, count, NULL);
 			for (unsigned int n = 0; n < count; n++)
 				check_individual_engine(i915, ctx, ci, n);
 		}
@@ -265,6 +298,123 @@ static void individual(int i915)
 	gem_context_destroy(i915, ctx);
 }
 
+static void bonded(int i915, unsigned int flags)
+#define CORK 0x1
+{
+	struct class_instance *master_engines;
+	uint32_t master;
+	struct bond {
+		uint64_t next_extension;
+		uint64_t name;
+
+		uint16_t class;
+		uint16_t inst;
+		uint32_t flags;
+		uint64_t mask;
+	} bonds[16];
+
+	/*
+	 * I915_CONTEXT_PARAM_ENGINE provides an extension that allows us
+	 * to specify which engine(s) to pair with a parallel (EXEC_SUBMIT)
+	 * request submitted to another engine.
+	 */
+
+	master = gem_queue_create(i915);
+
+	memset(bonds, 0, sizeof(bonds));
+	for (int n = 0; n < ARRAY_SIZE(bonds); n++) {
+		bonds[n].name = I915_CONTEXT_ENGINES_EXT_BOND;
+		bonds[n].next_extension =
+			n ? to_user_pointer(&bonds[n - 1]) : 0;
+		bonds[n].mask = 1 << n;
+	}
+
+	for (int mask = 0; mask < 32; mask++) {
+		unsigned int count, limit;
+		struct class_instance *siblings;
+		uint32_t ctx;
+		int n;
+
+		siblings = list_engines(i915, 1u << mask, &count);
+		if (!siblings)
+			continue;
+
+		if (count < 2) {
+			free(siblings);
+			continue;
+		}
+
+		igt_debug("Found %d engines of class %d\n", count, mask);
+
+		master_engines = list_engines(i915, ~(1u << mask), &limit);
+		set_engines(i915, master, master_engines, limit);
+
+		limit = min(count, limit);
+		for (n = 0; n < limit; n++) {
+			bonds[n].class = master_engines[n].class;
+			bonds[n].inst = master_engines[n].instance;
+		}
+
+		ctx = gem_context_clone(i915,
+				       	master, I915_CONTEXT_CLONE_VM,
+					I915_CONTEXT_CREATE_FLAGS_SINGLE_TIMELINE);
+		set_load_balancer(i915, ctx, siblings, count, &bonds[limit - 1]);
+
+		for (n = 0; n < limit; n++) {
+			struct drm_i915_gem_execbuffer2 eb;
+			IGT_CORK_HANDLE(cork);
+			igt_spin_t *spin, *plug;
+			double load;
+			int pmu;
+
+			igt_assert(siblings[n].class != master_engines[n].class);
+
+			pmu = perf_i915_open(I915_PMU_ENGINE_BUSY(siblings[n].class,
+								  siblings[n].instance));
+
+			plug = NULL;
+			if (flags & CORK) {
+				plug = __igt_spin_batch_new(i915,
+							    .ctx = master,
+							    .engine = n + 1,
+							    .dependency = igt_cork_plug(&cork, i915));
+			}
+
+			spin = __igt_spin_batch_new(i915,
+						    .ctx = master,
+						    .engine = n + 1,
+						    .flags = IGT_SPIN_FENCE_OUT);
+
+			eb = spin->execbuf;
+			eb.rsvd1 = ctx;
+			eb.rsvd2 = spin->out_fence;
+			eb.flags = I915_EXEC_FENCE_SUBMIT;
+			gem_execbuf(i915, &eb);
+
+			if (plug) {
+				igt_cork_unplug(&cork);
+				igt_spin_batch_free(i915, plug);
+			}
+
+			load = measure_load(pmu, 10000);
+			igt_spin_batch_free(i915, spin);
+
+			close(pmu);
+
+			igt_assert_f(load > 0.90,
+				     "engine %d (class:instance %d:%d) was found to be only %.1f%% busy\n",
+				     n, siblings[n].class, siblings[n].instance,
+				     load*100);
+		}
+
+		gem_context_destroy(i915, ctx);
+		free(master_engines);
+		free(siblings);
+	}
+
+	gem_context_destroy(i915, master);
+}
+
 static int add_pmu(int pmu, const struct class_instance *ci)
 {
 	return perf_i915_open_group(I915_PMU_ENGINE_BUSY(ci->class,
@@ -447,7 +597,7 @@ static void semaphore(int i915)
 		count = ARRAY_SIZE(block);
 
 		for (int i = 0; i < count; i++) {
-			set_load_balancer(i915, block[i], ci, count);
+			set_load_balancer(i915, block[i], ci, count, NULL);
 			spin[i] = __igt_spin_batch_new(i915,
 						       .ctx = block[i],
 						       .dependency = scratch);
@@ -458,7 +608,7 @@ static void semaphore(int i915)
 		 * or we let the vip through. If not, we hang.
 		 */
 		vip = gem_context_create(i915);
-		set_load_balancer(i915, vip, ci, count);
+		set_load_balancer(i915, vip, ci, count, NULL);
 		ping(i915, vip, 0);
 		gem_context_destroy(i915, vip);
 
@@ -573,7 +723,7 @@ static bool has_load_balancer(int i915)
 	int err;
 
 	ctx = gem_context_create(i915);
-	err = __set_load_balancer(i915, ctx, &ci, 1);
+	err = __set_load_balancer(i915, ctx, &ci, 1, NULL);
 	gem_context_destroy(i915, ctx);
 
 	return err == 0;
@@ -621,6 +771,12 @@ igt_main
 	igt_subtest("smoke")
 		smoketest(i915, 20);
 
+	igt_subtest("bonded-imm")
+		bonded(i915, 0);
+
+	igt_subtest("bonded-cork")
+		bonded(i915, CORK);
+
 	igt_fixture {
 		igt_stop_hang_detector();
 	}
-- 
2.20.1

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

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

* ✗ Fi.CI.BAT: failure for series starting with [01/19] i915/gem_ppgtt: Estimate resource usage and bail if it means swapping!
  2019-03-08 18:11 [PATCH i-g-t 01/19] i915/gem_ppgtt: Estimate resource usage and bail if it means swapping! Chris Wilson
                   ` (17 preceding siblings ...)
  2019-03-08 18:11 ` [PATCH i-g-t 19/19] i915/gem_exec_balancer: Exercise bonded pairs Chris Wilson
@ 2019-03-09  2:07 ` Patchwork
  2019-03-11 10:04 ` [PATCH i-g-t 01/19] " Mika Kuoppala
  19 siblings, 0 replies; 28+ messages in thread
From: Patchwork @ 2019-03-09  2:07 UTC (permalink / raw)
  To: Chris Wilson; +Cc: intel-gfx

== Series Details ==

Series: series starting with [01/19] i915/gem_ppgtt: Estimate resource usage and bail if it means swapping!
URL   : https://patchwork.freedesktop.org/series/57764/
State : failure

== Summary ==

CI Bug Log - changes from CI_DRM_5729 -> IGTPW_2581
====================================================

Summary
-------

  **FAILURE**

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

  External URL: https://patchwork.freedesktop.org/api/1.0/series/57764/revisions/1/mbox/

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

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

### IGT changes ###

#### Possible regressions ####

  * igt@i915_selftest@live_hangcheck:
    - fi-kbl-8809g:       PASS -> DMESG-FAIL

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

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

### IGT changes ###

#### Issues hit ####

  * igt@amdgpu/amd_basic@query-info:
    - fi-bsw-kefka:       NOTRUN -> SKIP [fdo#109271] +56

  * igt@gem_exec_basic@gtt-bsd2:
    - fi-byt-clapper:     NOTRUN -> SKIP [fdo#109271] +57

  * igt@gem_exec_basic@readonly-bsd1:
    - fi-snb-2520m:       NOTRUN -> SKIP [fdo#109271] +57

  * igt@i915_pm_rpm@basic-rte:
    - fi-bsw-kefka:       NOTRUN -> FAIL [fdo#108800]

  * igt@i915_selftest@live_evict:
    - fi-bsw-kefka:       NOTRUN -> DMESG-WARN [fdo#107709]

  * igt@i915_selftest@live_hangcheck:
    - fi-ilk-650:         PASS -> INCOMPLETE [fdo#109723]

  * igt@kms_addfb_basic@addfb25-y-tiled-small:
    - fi-byt-n2820:       NOTRUN -> SKIP [fdo#109271] +56

  * igt@kms_busy@basic-flip-a:
    - fi-bsw-n3050:       NOTRUN -> SKIP [fdo#109271] / [fdo#109278] +1

  * igt@kms_busy@basic-flip-b:
    - fi-gdg-551:         PASS -> FAIL [fdo#103182]

  * igt@kms_busy@basic-flip-c:
    - fi-byt-clapper:     NOTRUN -> SKIP [fdo#109271] / [fdo#109278]
    - fi-bsw-kefka:       NOTRUN -> SKIP [fdo#109271] / [fdo#109278]
    - fi-snb-2520m:       NOTRUN -> SKIP [fdo#109271] / [fdo#109278]
    - fi-byt-n2820:       NOTRUN -> SKIP [fdo#109271] / [fdo#109278]

  * igt@kms_chamelium@hdmi-crc-fast:
    - fi-bsw-n3050:       NOTRUN -> SKIP [fdo#109271] +62

  * igt@kms_chamelium@hdmi-edid-read:
    - fi-hsw-peppy:       NOTRUN -> SKIP [fdo#109271] +46

  * igt@kms_frontbuffer_tracking@basic:
    - fi-hsw-peppy:       NOTRUN -> DMESG-FAIL [fdo#102614] / [fdo#107814]
    - fi-byt-clapper:     NOTRUN -> FAIL [fdo#103167]

  * igt@kms_pipe_crc_basic@hang-read-crc-pipe-a:
    - fi-byt-clapper:     NOTRUN -> FAIL [fdo#103191] / [fdo#107362]

  * igt@runner@aborted:
    - fi-bsw-kefka:       NOTRUN -> FAIL [fdo#107709]

  
  [fdo#102614]: https://bugs.freedesktop.org/show_bug.cgi?id=102614
  [fdo#103167]: https://bugs.freedesktop.org/show_bug.cgi?id=103167
  [fdo#103182]: https://bugs.freedesktop.org/show_bug.cgi?id=103182
  [fdo#103191]: https://bugs.freedesktop.org/show_bug.cgi?id=103191
  [fdo#107362]: https://bugs.freedesktop.org/show_bug.cgi?id=107362
  [fdo#107709]: https://bugs.freedesktop.org/show_bug.cgi?id=107709
  [fdo#107814]: https://bugs.freedesktop.org/show_bug.cgi?id=107814
  [fdo#108800]: https://bugs.freedesktop.org/show_bug.cgi?id=108800
  [fdo#109271]: https://bugs.freedesktop.org/show_bug.cgi?id=109271
  [fdo#109278]: https://bugs.freedesktop.org/show_bug.cgi?id=109278
  [fdo#109723]: https://bugs.freedesktop.org/show_bug.cgi?id=109723


Participating hosts (40 -> 42)
------------------------------

  Additional (6): fi-bsw-n3050 fi-hsw-peppy fi-snb-2520m fi-bsw-kefka fi-byt-n2820 fi-byt-clapper 
  Missing    (4): fi-bsw-cyan fi-ilk-m540 fi-icl-u2 fi-bdw-samus 


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

    * IGT: IGT_4878 -> IGTPW_2581

  CI_DRM_5729: b50390674ed3eff49d1926a86acfee68b5565093 @ git://anongit.freedesktop.org/gfx-ci/linux
  IGTPW_2581: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_2581/
  IGT_4878: 478615b1edba88559386ba80ccbf0f035f3360a9 @ git://anongit.freedesktop.org/xorg/app/intel-gpu-tools



== Testlist changes ==

+++ 207 lines
--- 4 lines

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_2581/
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* Re: [PATCH i-g-t 17/19] i915: Add gem_ctx_engines
  2019-03-08 18:11 ` [PATCH i-g-t 17/19] i915: Add gem_ctx_engines Chris Wilson
@ 2019-03-11 10:00   ` Andi Shyti
  0 siblings, 0 replies; 28+ messages in thread
From: Andi Shyti @ 2019-03-11 10:00 UTC (permalink / raw)
  To: Chris Wilson; +Cc: intel-gfx

Hi Chris,

On Fri, Mar 08, 2019 at 06:11:27PM +0000, Chris Wilson wrote:
> To exercise the new I915_CONTEXT_PARAM_ENGINES and interactions with
> gem_execbuf().
> 
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> Cc: Tvrtko Ursulin <tvrtko.ursulin@linux.intel.com>
> Cc: Andi Shyti <andi@etezian.org>
> ---

I received three times this patch, could you please add a
versioning or at least a note, so that I understand the evolution
instead of checking version by version.

[...]

> +	engines->class_instance[0].engine_class = -1;
> +	igt_assert_eq(__gem_context_set_param(i915, &param), -ENOENT);
> +
> +	mprotect(engines, 4096, PROT_READ);

mprotect() can fail, do we want to check it?

[...]

> +	/* Unadulterated I915_EXEC_DEFAULT should work */
> +	execbuf.flags = 0;
> +	igt_assert_eq(__gem_execbuf(i915, &execbuf), 0);
> +	gem_sync(i915, obj.handle);
> +
> +	for_each_engine_class_instance(i915, e) {

mmhhh... we have it! I thought I was the first one :)

[...]

> +		for (int i = -1; i <= I915_EXEC_RING_MASK; i++) {
> +			igt_spin_t *spin;
> +
> +			memset(&engines, 0, sizeof(engines));
> +			engine_class(&engines, 0) = e->class;
> +			engine_instance(&engines, 0) = e->instance;

wouldn't it be easier to have an

   __u16 *class = &engines.class_instance[0].engine_class
   __u16 *instance = &e.class_instance[0].engine_instance
   
   *class = e->class;
   *instance = e->instance;

or something like

  engine_class_set(&engines, 0, e->class)
  engine_instance_set(&engines, 0, e->instance)

I find it more readable, because for me the '(' and ')' denote
functions.

[...]

> +				if (j == i) {
> +					igt_assert_f(err == 0,
> +						     "Failed to report the valid engine for slot %d\n",
> +						     i);
> +				} else {
> +					igt_assert_f(err == -EINVAL,
> +						     "Failed to report an invalid engine for slot %d (valid at %d)\n",
> +						     j, i);
> +				}

Standing to the kernel coding style, you should remove the braces
here.

From 'process/coding-style.rst'

    "Do not unnecessarily use braces where
     a single statement will do."

[...]

> +igt_main
> +{
> +	int i915 = -1;

fd? in a first glance it's not obvious that i915 is an fd.
Besides, it's in tests/i915/ I would expect it's a i915 file
descriptor.

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

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

* Re: [PATCH i-g-t 01/19] i915/gem_ppgtt: Estimate resource usage and bail if it means swapping!
  2019-03-08 18:11 [PATCH i-g-t 01/19] i915/gem_ppgtt: Estimate resource usage and bail if it means swapping! Chris Wilson
                   ` (18 preceding siblings ...)
  2019-03-09  2:07 ` ✗ Fi.CI.BAT: failure for series starting with [01/19] i915/gem_ppgtt: Estimate resource usage and bail if it means swapping! Patchwork
@ 2019-03-11 10:04 ` Mika Kuoppala
  2019-03-11 10:09   ` Chris Wilson
  19 siblings, 1 reply; 28+ messages in thread
From: Mika Kuoppala @ 2019-03-11 10:04 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx

Chris Wilson <chris@chris-wilson.co.uk> writes:

> fi-kbl-guc's swap ran dry while running blt-vs-render-ctxN, which is
> midly concerning but conceivable as we never checked there was enough
> memory to run the test to begin with.
>
> Each child needs to keep its own surface and possible a pair of logical
> contexts (one for rcs and one for bcs) so check that there is enough
> memory to allow all children to co-exist. During execution, we require
> another surface and batch, but these are temporary and so should fit
> fine with a small amount of thrashing on the boundary.
>
> References: https://bugs.freedesktop.org/show_bug.cgi?id=109801
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> ---
>  tests/i915/gem_ppgtt.c | 6 ++++++
>  1 file changed, 6 insertions(+)
>
> diff --git a/tests/i915/gem_ppgtt.c b/tests/i915/gem_ppgtt.c
> index 11ca31e74..9409bef14 100644
> --- a/tests/i915/gem_ppgtt.c
> +++ b/tests/i915/gem_ppgtt.c
> @@ -91,8 +91,14 @@ static void fork_rcs_copy(int timeout, uint32_t final,
>  #define CREATE_CONTEXT 0x1
>  {
>  	igt_render_copyfunc_t render_copy;
> +	uint64_t mem_per_child;
>  	int devid;
>  
> +	mem_per_child = SIZE;
> +	if (flags & CREATE_CONTEXT)
> +		mem_per_child += 2 * 128 * 1024; /* rough context sizes */

Someday libify this so that we give num context and size per each?
Just that the modern gens eat more so we could tweak per gen instead
of going with the max estimate. Regardless,

Reviewed-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>


> +	intel_require_memory(mem_per_child, count, CHECK_RAM);
> +
>  	for (int child = 0; child < count; child++) {
>  		int fd = drm_open_driver(DRIVER_INTEL);
>  		drm_intel_bufmgr *bufmgr;
> -- 
> 2.20.1
>
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/intel-gfx
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* Re: [PATCH i-g-t 01/19] i915/gem_ppgtt: Estimate resource usage and bail if it means swapping!
  2019-03-11 10:04 ` [PATCH i-g-t 01/19] " Mika Kuoppala
@ 2019-03-11 10:09   ` Chris Wilson
  0 siblings, 0 replies; 28+ messages in thread
From: Chris Wilson @ 2019-03-11 10:09 UTC (permalink / raw)
  To: Mika Kuoppala, intel-gfx

Quoting Mika Kuoppala (2019-03-11 10:04:05)
> Chris Wilson <chris@chris-wilson.co.uk> writes:
> > +     mem_per_child = SIZE;
> > +     if (flags & CREATE_CONTEXT)
> > +             mem_per_child += 2 * 128 * 1024; /* rough context sizes */
> 
> Someday libify this so that we give num context and size per each?
> Just that the modern gens eat more so we could tweak per gen instead
> of going with the max estimate. Regardless,

You mean keep guessing something the kernel knows? If only we had an
interface for that ;)
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* Re: [PATCH i-g-t 19/19] i915/gem_exec_balancer: Exercise bonded pairs
  2019-03-08 18:11 ` [PATCH i-g-t 19/19] i915/gem_exec_balancer: Exercise bonded pairs Chris Wilson
@ 2019-03-11 10:18   ` Andi Shyti
  2019-03-11 10:30   ` Andi Shyti
  1 sibling, 0 replies; 28+ messages in thread
From: Andi Shyti @ 2019-03-11 10:18 UTC (permalink / raw)
  To: Chris Wilson; +Cc: intel-gfx

Hi Chris,

just a nitpick, a warning that came out when I applied the
patches.

>  	struct i915_context_engines_load_balance balancer = {
> -		{ .name = I915_CONTEXT_ENGINES_EXT_LOAD_BALANCE },
> +		{ 
                 ^
trailing whitespace

> +			.next_extension = to_user_pointer(ext),
> +			.name = I915_CONTEXT_ENGINES_EXT_LOAD_BALANCE
> +	       	},
         ^^^^^^|
               +-> tab

spaces before tab

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

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

* Re: [PATCH i-g-t 19/19] i915/gem_exec_balancer: Exercise bonded pairs
  2019-03-08 18:11 ` [PATCH i-g-t 19/19] i915/gem_exec_balancer: Exercise bonded pairs Chris Wilson
  2019-03-11 10:18   ` Andi Shyti
@ 2019-03-11 10:30   ` Andi Shyti
  2019-03-11 10:39     ` Chris Wilson
  1 sibling, 1 reply; 28+ messages in thread
From: Andi Shyti @ 2019-03-11 10:30 UTC (permalink / raw)
  To: Chris Wilson; +Cc: intel-gfx

Hi again,

I was thinking...

> +static void bonded(int i915, unsigned int flags)
> +#define CORK 0x1
> +{
> +	struct class_instance *master_engines;

... shall we just make 'struct class_instance' generic in the
uapi? I do not expect every test that uses class and instance to
define its own structure.

Besides we can avoid a good load of 'offsetof' and make the whole
thing more readable.

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

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

* Re: [PATCH i-g-t 19/19] i915/gem_exec_balancer: Exercise bonded pairs
  2019-03-11 10:30   ` Andi Shyti
@ 2019-03-11 10:39     ` Chris Wilson
  0 siblings, 0 replies; 28+ messages in thread
From: Chris Wilson @ 2019-03-11 10:39 UTC (permalink / raw)
  To: Andi Shyti; +Cc: intel-gfx

Quoting Andi Shyti (2019-03-11 10:30:23)
> Hi again,
> 
> I was thinking...
> 
> > +static void bonded(int i915, unsigned int flags)
> > +#define CORK 0x1
> > +{
> > +     struct class_instance *master_engines;
> 
> ... shall we just make 'struct class_instance' generic in the
> uapi? I do not expect every test that uses class and instance to
> define its own structure.

But this test is defining its own struct ;)

> Besides we can avoid a good load of 'offsetof' and make the whole
> thing more readable.

Maybe stop using uAPI structs for library structs -- it's not a good
strategy long term. (We only need the uAPI for interfacing, but for the
library we tend to want other details such as mmio_base, name, etc)

Using offsetof to define the size is reasonably common practice for
variable length arrays (except unless you are paranoid about overflow in
every step of the computation).
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* Re: [PATCH i-g-t 18/19] i915: Add gem_exec_balancer
  2019-03-08 18:11 ` [PATCH i-g-t 18/19] i915: Add gem_exec_balancer Chris Wilson
@ 2019-03-12 10:23   ` Tvrtko Ursulin
  2019-03-12 10:27     ` Chris Wilson
  0 siblings, 1 reply; 28+ messages in thread
From: Tvrtko Ursulin @ 2019-03-12 10:23 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx


On 08/03/2019 18:11, Chris Wilson wrote:
> Exercise the in-kernel load balancer checking that we can distribute
> batches across the set of ctx->engines to avoid load.
> 
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> ---
>   tests/Makefile.am              |   1 +
>   tests/Makefile.sources         |   1 +
>   tests/i915/gem_exec_balancer.c | 627 +++++++++++++++++++++++++++++++++
>   tests/meson.build              |   7 +
>   4 files changed, 636 insertions(+)
>   create mode 100644 tests/i915/gem_exec_balancer.c
> 
> diff --git a/tests/Makefile.am b/tests/Makefile.am
> index 289249b42..68a9c14bf 100644
> --- a/tests/Makefile.am
> +++ b/tests/Makefile.am
> @@ -102,6 +102,7 @@ gem_close_race_LDADD = $(LDADD) -lpthread
>   gem_ctx_thrash_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
>   gem_ctx_thrash_LDADD = $(LDADD) -lpthread
>   gem_ctx_sseu_LDADD = $(LDADD) $(top_builddir)/lib/libigt_perf.la
> +i915_gem_exec_balancer_LDADD = $(LDADD) $(top_builddir)/lib/libigt_perf.la
>   gem_exec_capture_LDADD = $(LDADD) -lz
>   gem_exec_parallel_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
>   gem_exec_parallel_LDADD = $(LDADD) -lpthread
> diff --git a/tests/Makefile.sources b/tests/Makefile.sources
> index 41e756f15..f6c21a1aa 100644
> --- a/tests/Makefile.sources
> +++ b/tests/Makefile.sources
> @@ -23,6 +23,7 @@ TESTS_progs = \
>   	drm_read \
>   	i915/gem_ctx_engines \
>   	i915/gem_ctx_shared \
> +	i915/gem_exec_balancer \
>   	kms_3d \
>   	kms_addfb_basic \
>   	kms_atomic \
> diff --git a/tests/i915/gem_exec_balancer.c b/tests/i915/gem_exec_balancer.c
> new file mode 100644
> index 000000000..d9fdffe67
> --- /dev/null
> +++ b/tests/i915/gem_exec_balancer.c
> @@ -0,0 +1,627 @@
> +/*
> + * Copyright © 2018 Intel Corporation
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the next
> + * paragraph) shall be included in all copies or substantial portions of the
> + * Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
> + * IN THE SOFTWARE.
> + */
> +
> +#include <sched.h>
> +
> +#include "igt.h"
> +#include "igt_perf.h"
> +#include "i915/gem_ring.h"
> +#include "sw_sync.h"
> +
> +IGT_TEST_DESCRIPTION("Exercise in-kernel load-balancing");
> +
> +struct class_instance {
> +	uint16_t class;
> +	uint16_t instance;
> +};
> +#define INSTANCE_COUNT (1 << I915_PMU_SAMPLE_INSTANCE_BITS)
> +
> +static bool has_class_instance(int i915, uint16_t class, uint16_t instance)
> +{
> +	int fd;
> +
> +	fd = perf_i915_open(I915_PMU_ENGINE_BUSY(class, instance));
> +	if (fd != -1) {
> +		close(fd);
> +		return true;
> +	}
> +
> +	return false;
> +}
> +
> +static struct class_instance *
> +list_engines(int i915, uint32_t class_mask, unsigned int *out)
> +{
> +	unsigned int count = 0, size = 64;
> +	struct class_instance *engines;
> +
> +	engines = malloc(size * sizeof(*engines));
> +	if (!engines) {
> +		*out = 0;
> +		return NULL;
> +	}
> +
> +	for (enum drm_i915_gem_engine_class class = I915_ENGINE_CLASS_RENDER;
> +	     class_mask;
> +	     class++, class_mask >>= 1) {
> +		if (!(class_mask & 1))
> +			continue;
> +
> +		for (unsigned int instance = 0;
> +		     instance < INSTANCE_COUNT;
> +		     instance++) {
> +		     if (!has_class_instance(i915, class, instance))
> +			     continue;
> +
> +			if (count == size) {
> +				struct class_instance *e;
> +
> +				size *= 2;
> +				e = realloc(engines, size*sizeof(*engines));
> +				if (!e) {
> +					*out = count;
> +					return engines;
> +				}
> +
> +				engines = e;
> +			}
> +
> +			engines[count++] = (struct class_instance){
> +				.class = class,
> +				.instance = instance,
> +			};
> +		}
> +	}
> +
> +	if (!count) {
> +		free(engines);
> +		engines = NULL;
> +	}
> +
> +	*out = count;
> +	return engines;
> +}
> +
> +static int __set_load_balancer(int i915, uint32_t ctx,
> +			       const struct class_instance *ci,
> +			       unsigned int count)
> +{
> +	struct i915_context_engines_load_balance balancer = {
> +		{ .name = I915_CONTEXT_ENGINES_EXT_LOAD_BALANCE },
> +		.engines_mask = ~0ull,
> +	};
> +	I915_DEFINE_CONTEXT_PARAM_ENGINES(engines, count + 1);
> +	struct drm_i915_gem_context_param p = {
> +		.ctx_id = ctx,
> +		.param = I915_CONTEXT_PARAM_ENGINES,
> +		.size = sizeof(&engines),

sizeof(engines)

Regards,

Tvrtko

> +		.value = to_user_pointer(&engines)
> +	};
> +
> +	engines.extensions = to_user_pointer(&balancer),
> +	engines.class_instance[0].engine_class = I915_ENGINE_CLASS_INVALID;
> +	engines.class_instance[0].engine_instance = I915_ENGINE_CLASS_INVALID_NONE;
> +	memcpy(engines.class_instance + 1, ci, count * sizeof(uint32_t));
> +
> +	return __gem_context_set_param(i915, &p);
> +}
> +
> +static void set_load_balancer(int i915, uint32_t ctx,
> +			      const struct class_instance *ci,
> +			      unsigned int count)
> +{
> +	igt_assert_eq(__set_load_balancer(i915, ctx, ci, count), 0);
> +}
> +
> +static uint32_t load_balancer_create(int i915,
> +				     const struct class_instance *ci,
> +				     unsigned int count)
> +{
> +	uint32_t ctx;
> +
> +	ctx = gem_context_create(i915);
> +	set_load_balancer(i915, ctx, ci, count);
> +
> +	return ctx;
> +}
> +
> +static void kick_kthreads(int period_us)
> +{
> +	sched_yield();
> +	usleep(period_us);
> +}
> +
> +static double measure_load(int pmu, int period_us)
> +{
> +	uint64_t data[2];
> +	uint64_t d_t, d_v;
> +
> +	kick_kthreads(period_us);
> +
> +	igt_assert_eq(read(pmu, data, sizeof(data)), sizeof(data));
> +	d_v = -data[0];
> +	d_t = -data[1];
> +
> +	usleep(period_us);
> +
> +	igt_assert_eq(read(pmu, data, sizeof(data)), sizeof(data));
> +	d_v += data[0];
> +	d_t += data[1];
> +
> +	return d_v / (double)d_t;
> +}
> +
> +static double measure_min_load(int pmu, unsigned int num, int period_us)
> +{
> +	uint64_t data[2 + num];
> +	uint64_t d_t, d_v[num];
> +	uint64_t min = -1, max = 0;
> +
> +	kick_kthreads(period_us);
> +
> +	igt_assert_eq(read(pmu, data, sizeof(data)), sizeof(data));
> +	for (unsigned int n = 0; n < num; n++)
> +		d_v[n] = -data[2 + n];
> +	d_t = -data[1];
> +
> +	usleep(period_us);
> +
> +	igt_assert_eq(read(pmu, data, sizeof(data)), sizeof(data));
> +
> +	d_t += data[1];
> +	for (unsigned int n = 0; n < num; n++) {
> +		d_v[n] += data[2 + n];
> +		igt_debug("engine[%d]: %.1f%%\n",
> +			  n, d_v[n] / (double)d_t * 100);
> +		if (d_v[n] < min)
> +			min = d_v[n];
> +		if (d_v[n] > max)
> +			max = d_v[n];
> +	}
> +
> +	igt_debug("elapsed: %"PRIu64"ns, load [%.1f, %.1f]%%\n",
> +		  d_t, min / (double)d_t * 100,  max / (double)d_t * 100);
> +
> +	return min / (double)d_t;
> +}
> +
> +static void check_individual_engine(int i915,
> +				    uint32_t ctx,
> +				    const struct class_instance *ci,
> +				    int idx)
> +{
> +	igt_spin_t *spin;
> +	double load;
> +	int pmu;
> +
> +	pmu = perf_i915_open(I915_PMU_ENGINE_BUSY(ci[idx].class,
> +						  ci[idx].instance));
> +
> +	spin = igt_spin_batch_new(i915, .ctx = ctx, .engine = idx + 1);
> +	load = measure_load(pmu, 10000);
> +	igt_spin_batch_free(i915, spin);
> +
> +	close(pmu);
> +
> +	igt_assert_f(load > 0.90,
> +		     "engine %d (class:instance %d:%d) was found to be only %.1f%% busy\n",
> +		     idx, ci[idx].class, ci[idx].instance, load*100);
> +}
> +
> +static void individual(int i915)
> +{
> +	uint32_t ctx;
> +
> +	/*
> +	 * I915_CONTEXT_PARAM_ENGINE allows us to index into the user
> +	 * supplied array from gem_execbuf(). Our check is to build the
> +	 * ctx->engine[] with various different engine classes, feed in
> +	 * a spinner and then ask pmu to confirm it the expected engine
> +	 * was busy.
> +	 */
> +
> +	ctx = gem_context_create(i915);
> +
> +	for (int mask = 0; mask < 32; mask++) {
> +		struct class_instance *ci;
> +		unsigned int count;
> +
> +		ci = list_engines(i915, 1u << mask, &count);
> +		if (!ci)
> +			continue;
> +
> +		igt_debug("Found %d engines of class %d\n", count, mask);
> +
> +		for (int pass = 0; pass < count; pass++) { /* approx. count! */
> +			igt_permute_array(ci, count, igt_exchange_int64);
> +			set_load_balancer(i915, ctx, ci, count);
> +			for (unsigned int n = 0; n < count; n++)
> +				check_individual_engine(i915, ctx, ci, n);
> +		}
> +
> +		free(ci);
> +	}
> +
> +	gem_context_destroy(i915, ctx);
> +}
> +
> +static int add_pmu(int pmu, const struct class_instance *ci)
> +{
> +	return perf_i915_open_group(I915_PMU_ENGINE_BUSY(ci->class,
> +							 ci->instance),
> +				    pmu);
> +}
> +
> +static uint32_t __batch_create(int i915, uint32_t offset)
> +{
> +	const uint32_t bbe = MI_BATCH_BUFFER_END;
> +	uint32_t handle;
> +
> +	handle = gem_create(i915, ALIGN(offset + 4, 4096));
> +	gem_write(i915, handle, offset, &bbe, sizeof(bbe));
> +
> +	return handle;
> +}
> +
> +static uint32_t batch_create(int i915)
> +{
> +	return __batch_create(i915, 0);
> +}
> +
> +static void full(int i915, unsigned int flags)
> +#define PULSE 0x1
> +#define LATE 0x2
> +{
> +	struct drm_i915_gem_exec_object2 batch = {
> +		.handle = batch_create(i915),
> +	};
> +
> +	if (flags & LATE)
> +		igt_require_sw_sync();
> +
> +	/*
> +	 * I915_CONTEXT_PARAM_ENGINE changes the meaning of I915_EXEC_DEFAULT
> +	 * to provide an automatic selection from the ctx->engine[]. It
> +	 * employs load-balancing to evenly distribute the workload the
> +	 * array. If we submit N spinners, we expect them to be simultaneously
> +	 * running across N engines and use PMU to confirm that the entire
> +	 * set of engines are busy.
> +	 *
> +	 * We complicate matters by interpersing shortlived tasks to challenge
> +	 * the kernel to search for space in which to insert new batches.
> +	 */
> +
> +
> +	for (int mask = 0; mask < 32; mask++) {
> +		struct class_instance *ci;
> +		igt_spin_t *spin = NULL;
> +		unsigned int count;
> +		IGT_CORK_FENCE(cork);
> +		double load;
> +		int fence = -1;
> +		int *pmu;
> +
> +		ci = list_engines(i915, 1u << mask, &count);
> +		if (!ci)
> +			continue;
> +
> +		igt_debug("Found %d engines of class %d\n", count, mask);
> +
> +		pmu = malloc(sizeof(*pmu) * count);
> +		igt_assert(pmu);
> +
> +		if (flags & LATE)
> +			fence = igt_cork_plug(&cork, i915);
> +
> +		pmu[0] = -1;
> +		for (unsigned int n = 0; n < count; n++) {
> +			uint32_t ctx;
> +
> +			pmu[n] = add_pmu(pmu[0], &ci[n]);
> +
> +			if (flags & PULSE) {
> +				struct drm_i915_gem_execbuffer2 eb = {
> +					.buffers_ptr = to_user_pointer(&batch),
> +					.buffer_count = 1,
> +					.rsvd2 = fence,
> +					.flags = flags & LATE ? I915_EXEC_FENCE_IN : 0,
> +				};
> +
> +				gem_execbuf(i915, &eb);
> +			}
> +
> +			/*
> +			 * Each spinner needs to be one a new timeline,
> +			 * otherwise they will just sit in the single queue
> +			 * and not run concurrently.
> +			 */
> +			ctx = load_balancer_create(i915, ci, count);
> +
> +			if (spin == NULL) {
> +				spin = __igt_spin_batch_new(i915, ctx, 0, 0);
> +			} else {
> +				struct drm_i915_gem_exec_object2 obj = {
> +					.handle = spin->handle,
> +				};
> +				struct drm_i915_gem_execbuffer2 eb = {
> +					.buffers_ptr = to_user_pointer(&obj),
> +					.buffer_count = 1,
> +					.rsvd1 = ctx,
> +					.rsvd2 = fence,
> +					.flags = flags & LATE ? I915_EXEC_FENCE_IN : 0,
> +				};
> +
> +				gem_execbuf(i915, &eb);
> +			}
> +
> +			gem_context_destroy(i915, ctx);
> +		}
> +
> +		if (flags & LATE) {
> +			igt_cork_unplug(&cork);
> +			close(fence);
> +		}
> +
> +		load = measure_min_load(pmu[0], count, 10000);
> +		igt_spin_batch_free(i915, spin);
> +
> +		close(pmu[0]);
> +		free(pmu);
> +
> +		free(ci);
> +
> +		igt_assert_f(load > 0.90,
> +			     "minimum load for %d x class:%d was found to be only %.1f%% busy\n",
> +			     count, mask, load*100);
> +	}
> +
> +	gem_close(i915, batch.handle);
> +}
> +
> +static void ping(int i915, uint32_t ctx, unsigned int engine)
> +{
> +	struct drm_i915_gem_exec_object2 obj = {
> +		.handle = batch_create(i915),
> +	};
> +	struct drm_i915_gem_execbuffer2 execbuf = {
> +		.buffers_ptr = to_user_pointer(&obj),
> +		.buffer_count = 1,
> +		.flags = engine,
> +		.rsvd1 = ctx,
> +	};
> +	gem_execbuf(i915, &execbuf);
> +	gem_sync(i915, obj.handle);
> +	gem_close(i915, obj.handle);
> +}
> +
> +static void semaphore(int i915)
> +{
> +	uint32_t block[2], scratch;
> +	igt_spin_t *spin[3];
> +
> +	/*
> +	 * If we are using HW semaphores to launch serialised requests
> +	 * on different engine concurrently, we want to verify that real
> +	 * work is unimpeded.
> +	 */
> +	igt_require(gem_scheduler_has_preemption(i915));
> +
> +	block[0] = gem_context_create(i915);
> +	block[1] = gem_context_create(i915);
> +
> +	scratch = gem_create(i915, 4096);
> +	spin[2] = igt_spin_batch_new(i915, .dependency = scratch);
> +	for (int mask = 1; mask < 32; mask++) {
> +		struct class_instance *ci;
> +		unsigned int count;
> +		uint32_t vip;
> +
> +		ci = list_engines(i915, 1u << mask, &count);
> +		if (!ci)
> +			continue;
> +
> +		if (count < ARRAY_SIZE(block))
> +			continue;
> +
> +		/* Ensure that we completely occupy all engines in this group */
> +		count = ARRAY_SIZE(block);
> +
> +		for (int i = 0; i < count; i++) {
> +			set_load_balancer(i915, block[i], ci, count);
> +			spin[i] = __igt_spin_batch_new(i915,
> +						       .ctx = block[i],
> +						       .dependency = scratch);
> +		}
> +
> +		/*
> +		 * Either we haven't blocked both engines with semaphores,
> +		 * or we let the vip through. If not, we hang.
> +		 */
> +		vip = gem_context_create(i915);
> +		set_load_balancer(i915, vip, ci, count);
> +		ping(i915, vip, 0);
> +		gem_context_destroy(i915, vip);
> +
> +		for (int i = 0; i < count; i++)
> +			igt_spin_batch_free(i915, spin[i]);
> +
> +		free(ci);
> +	}
> +	igt_spin_batch_free(i915, spin[2]);
> +	gem_close(i915, scratch);
> +
> +	gem_context_destroy(i915, block[1]);
> +	gem_context_destroy(i915, block[0]);
> +}
> +
> +static void smoketest(int i915, int timeout)
> +{
> +	struct drm_i915_gem_exec_object2 batch[2] = {
> +		{ .handle = __batch_create(i915, 16380) }
> +	};
> +	unsigned int ncontext = 0;
> +	uint32_t *contexts = NULL;
> +	uint32_t *handles = NULL;
> +
> +	igt_require_sw_sync();
> +
> +	for (int mask = 0; mask < 32; mask++) {
> +		struct class_instance *ci;
> +		unsigned int count = 0;
> +
> +		ci = list_engines(i915, 1u << mask, &count);
> +		if (!ci || count < 2) {
> +			free(ci);
> +			continue;
> +		}
> +
> +		igt_debug("Found %d engines of class %d\n", count, mask);
> +
> +		ncontext += 128;
> +		contexts = realloc(contexts, sizeof(*contexts) * ncontext);
> +		igt_assert(contexts);
> +
> +		for (unsigned int n = ncontext - 128; n < ncontext; n++) {
> +			contexts[n] = load_balancer_create(i915, ci, count);
> +			igt_assert(contexts[n]);
> +		}
> +
> +		free(ci);
> +	}
> +	igt_debug("Created %d virtual engines (one per context)\n", ncontext);
> +	igt_require(ncontext);
> +
> +	contexts = realloc(contexts, sizeof(*contexts) * ncontext * 4);
> +	igt_assert(contexts);
> +	memcpy(contexts + ncontext, contexts, ncontext * sizeof(*contexts));
> +	ncontext *= 2;
> +	memcpy(contexts + ncontext, contexts, ncontext * sizeof(*contexts));
> +	ncontext *= 2;
> +
> +	handles = malloc(sizeof(*handles) * ncontext);
> +	igt_assert(handles);
> +	for (unsigned int n = 0; n < ncontext; n++)
> +		handles[n] = gem_create(i915, 4096);
> +
> +	igt_until_timeout(timeout) {
> +		unsigned int count = 1 + (rand() % (ncontext - 1));
> +		IGT_CORK_FENCE(cork);
> +		int fence = igt_cork_plug(&cork, i915);
> +
> +		for (unsigned int n = 0; n < count; n++) {
> +			struct drm_i915_gem_execbuffer2 eb = {
> +				.buffers_ptr = to_user_pointer(batch),
> +				.buffer_count = ARRAY_SIZE(batch),
> +				.rsvd1 = contexts[n],
> +				.rsvd2 = fence,
> +				.flags = I915_EXEC_BATCH_FIRST | I915_EXEC_FENCE_IN,
> +			};
> +			batch[1].handle = handles[n];
> +			gem_execbuf(i915, &eb);
> +		}
> +		igt_permute_array(handles, count, igt_exchange_int);
> +
> +		igt_cork_unplug(&cork);
> +		for (unsigned int n = 0; n < count; n++)
> +			gem_sync(i915, handles[n]);
> +
> +		close(fence);
> +	}
> +
> +	for (unsigned int n = 0; n < ncontext; n++) {
> +		gem_close(i915, handles[n]);
> +		__gem_context_destroy(i915, contexts[n]);
> +	}
> +	free(handles);
> +	free(contexts);
> +	gem_close(i915, batch[0].handle);
> +}
> +
> +static bool has_context_engines(int i915)
> +{
> +	struct drm_i915_gem_context_param p = {
> +		.param = I915_CONTEXT_PARAM_ENGINES,
> +	};
> +
> +	return __gem_context_set_param(i915, &p) == 0;
> +}
> +
> +static bool has_load_balancer(int i915)
> +{
> +	struct class_instance ci = {};
> +	uint32_t ctx;
> +	int err;
> +
> +	ctx = gem_context_create(i915);
> +	err = __set_load_balancer(i915, ctx, &ci, 1);
> +	gem_context_destroy(i915, ctx);
> +
> +	return err == 0;
> +}
> +
> +igt_main
> +{
> +	int i915 = -1;
> +
> +	igt_skip_on_simulation();
> +
> +	igt_fixture {
> +		i915 = drm_open_driver(DRIVER_INTEL);
> +		igt_require_gem(i915);
> +
> +		gem_require_contexts(i915);
> +		igt_require(has_context_engines(i915));
> +		igt_require(has_load_balancer(i915));
> +
> +		igt_fork_hang_detector(i915);
> +	}
> +
> +	igt_subtest("individual")
> +		individual(i915);
> +
> +	igt_subtest_group {
> +		static const struct {
> +			const char *name;
> +			unsigned int flags;
> +		} phases[] = {
> +			{ "", 0 },
> +			{ "-pulse", PULSE },
> +			{ "-late", LATE },
> +			{ "-late-pulse", PULSE | LATE },
> +			{ }
> +		};
> +		for (typeof(*phases) *p = phases; p->name; p++)
> +			igt_subtest_f("full%s", p->name)
> +				full(i915, p->flags);
> +	}
> +
> +	igt_subtest("semaphore")
> +		semaphore(i915);
> +
> +	igt_subtest("smoke")
> +		smoketest(i915, 20);
> +
> +	igt_fixture {
> +		igt_stop_hang_detector();
> +	}
> +}
> diff --git a/tests/meson.build b/tests/meson.build
> index 0539c20c8..bd2db825e 100644
> --- a/tests/meson.build
> +++ b/tests/meson.build
> @@ -293,6 +293,13 @@ test_executables += executable('gem_eio',
>   	   install : true)
>   test_list += 'gem_eio'
>   
> +test_executables += executable('gem_exec_balancer', 'i915/gem_exec_balancer.c',
> +	   dependencies : test_deps + [ lib_igt_perf ],
> +	   install_dir : libexecdir,
> +	   install_rpath : libexecdir_rpathdir,
> +	   install : true)
> +test_progs += 'gem_exec_balancer'
> +
>   test_executables += executable('gem_mocs_settings',
>   	   join_paths('i915', 'gem_mocs_settings.c'),
>   	   dependencies : test_deps + [ lib_igt_perf ],
> 
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* Re: [PATCH i-g-t 18/19] i915: Add gem_exec_balancer
  2019-03-12 10:23   ` Tvrtko Ursulin
@ 2019-03-12 10:27     ` Chris Wilson
  0 siblings, 0 replies; 28+ messages in thread
From: Chris Wilson @ 2019-03-12 10:27 UTC (permalink / raw)
  To: Tvrtko Ursulin, intel-gfx

Quoting Tvrtko Ursulin (2019-03-12 10:23:12)
> 
> On 08/03/2019 18:11, Chris Wilson wrote:
> > Exercise the in-kernel load balancer checking that we can distribute
> > batches across the set of ctx->engines to avoid load.
> > 
> > Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> > ---
> >   tests/Makefile.am              |   1 +
> >   tests/Makefile.sources         |   1 +
> >   tests/i915/gem_exec_balancer.c | 627 +++++++++++++++++++++++++++++++++
> >   tests/meson.build              |   7 +
> >   4 files changed, 636 insertions(+)
> >   create mode 100644 tests/i915/gem_exec_balancer.c
> > 
> > diff --git a/tests/Makefile.am b/tests/Makefile.am
> > index 289249b42..68a9c14bf 100644
> > --- a/tests/Makefile.am
> > +++ b/tests/Makefile.am
> > @@ -102,6 +102,7 @@ gem_close_race_LDADD = $(LDADD) -lpthread
> >   gem_ctx_thrash_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
> >   gem_ctx_thrash_LDADD = $(LDADD) -lpthread
> >   gem_ctx_sseu_LDADD = $(LDADD) $(top_builddir)/lib/libigt_perf.la
> > +i915_gem_exec_balancer_LDADD = $(LDADD) $(top_builddir)/lib/libigt_perf.la
> >   gem_exec_capture_LDADD = $(LDADD) -lz
> >   gem_exec_parallel_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
> >   gem_exec_parallel_LDADD = $(LDADD) -lpthread
> > diff --git a/tests/Makefile.sources b/tests/Makefile.sources
> > index 41e756f15..f6c21a1aa 100644
> > --- a/tests/Makefile.sources
> > +++ b/tests/Makefile.sources
> > @@ -23,6 +23,7 @@ TESTS_progs = \
> >       drm_read \
> >       i915/gem_ctx_engines \
> >       i915/gem_ctx_shared \
> > +     i915/gem_exec_balancer \
> >       kms_3d \
> >       kms_addfb_basic \
> >       kms_atomic \
> > diff --git a/tests/i915/gem_exec_balancer.c b/tests/i915/gem_exec_balancer.c
> > new file mode 100644
> > index 000000000..d9fdffe67
> > --- /dev/null
> > +++ b/tests/i915/gem_exec_balancer.c
> > @@ -0,0 +1,627 @@
> > +/*
> > + * Copyright © 2018 Intel Corporation
> > + *
> > + * Permission is hereby granted, free of charge, to any person obtaining a
> > + * copy of this software and associated documentation files (the "Software"),
> > + * to deal in the Software without restriction, including without limitation
> > + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> > + * and/or sell copies of the Software, and to permit persons to whom the
> > + * Software is furnished to do so, subject to the following conditions:
> > + *
> > + * The above copyright notice and this permission notice (including the next
> > + * paragraph) shall be included in all copies or substantial portions of the
> > + * Software.
> > + *
> > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> > + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> > + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
> > + * IN THE SOFTWARE.
> > + */
> > +
> > +#include <sched.h>
> > +
> > +#include "igt.h"
> > +#include "igt_perf.h"
> > +#include "i915/gem_ring.h"
> > +#include "sw_sync.h"
> > +
> > +IGT_TEST_DESCRIPTION("Exercise in-kernel load-balancing");
> > +
> > +struct class_instance {
> > +     uint16_t class;
> > +     uint16_t instance;
> > +};
> > +#define INSTANCE_COUNT (1 << I915_PMU_SAMPLE_INSTANCE_BITS)
> > +
> > +static bool has_class_instance(int i915, uint16_t class, uint16_t instance)
> > +{
> > +     int fd;
> > +
> > +     fd = perf_i915_open(I915_PMU_ENGINE_BUSY(class, instance));
> > +     if (fd != -1) {
> > +             close(fd);
> > +             return true;
> > +     }
> > +
> > +     return false;
> > +}
> > +
> > +static struct class_instance *
> > +list_engines(int i915, uint32_t class_mask, unsigned int *out)
> > +{
> > +     unsigned int count = 0, size = 64;
> > +     struct class_instance *engines;
> > +
> > +     engines = malloc(size * sizeof(*engines));
> > +     if (!engines) {
> > +             *out = 0;
> > +             return NULL;
> > +     }
> > +
> > +     for (enum drm_i915_gem_engine_class class = I915_ENGINE_CLASS_RENDER;
> > +          class_mask;
> > +          class++, class_mask >>= 1) {
> > +             if (!(class_mask & 1))
> > +                     continue;
> > +
> > +             for (unsigned int instance = 0;
> > +                  instance < INSTANCE_COUNT;
> > +                  instance++) {
> > +                  if (!has_class_instance(i915, class, instance))
> > +                          continue;
> > +
> > +                     if (count == size) {
> > +                             struct class_instance *e;
> > +
> > +                             size *= 2;
> > +                             e = realloc(engines, size*sizeof(*engines));
> > +                             if (!e) {
> > +                                     *out = count;
> > +                                     return engines;
> > +                             }
> > +
> > +                             engines = e;
> > +                     }
> > +
> > +                     engines[count++] = (struct class_instance){
> > +                             .class = class,
> > +                             .instance = instance,
> > +                     };
> > +             }
> > +     }
> > +
> > +     if (!count) {
> > +             free(engines);
> > +             engines = NULL;
> > +     }
> > +
> > +     *out = count;
> > +     return engines;
> > +}
> > +
> > +static int __set_load_balancer(int i915, uint32_t ctx,
> > +                            const struct class_instance *ci,
> > +                            unsigned int count)
> > +{
> > +     struct i915_context_engines_load_balance balancer = {
> > +             { .name = I915_CONTEXT_ENGINES_EXT_LOAD_BALANCE },
> > +             .engines_mask = ~0ull,
> > +     };
> > +     I915_DEFINE_CONTEXT_PARAM_ENGINES(engines, count + 1);
> > +     struct drm_i915_gem_context_param p = {
> > +             .ctx_id = ctx,
> > +             .param = I915_CONTEXT_PARAM_ENGINES,
> > +             .size = sizeof(&engines),
> 
> sizeof(engines)

Ta. Don't look at the bonding test -- that still has all the +1 offset
baked in. ;)
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

end of thread, other threads:[~2019-03-12 10:27 UTC | newest]

Thread overview: 28+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-03-08 18:11 [PATCH i-g-t 01/19] i915/gem_ppgtt: Estimate resource usage and bail if it means swapping! Chris Wilson
2019-03-08 18:11 ` [PATCH i-g-t 02/19] lib/i915: Pretty print HW semaphores Chris Wilson
2019-03-08 18:11 ` [PATCH i-g-t 03/19] lib: Add GPU power measurement Chris Wilson
2019-03-08 18:11 ` [PATCH i-g-t 04/19] i915/gem_exec_schedule: Measure semaphore power consumption Chris Wilson
2019-03-08 18:11 ` [PATCH i-g-t 05/19] i915/gem_exec_whisper: Measure total power consumed Chris Wilson
2019-03-08 18:11 ` [PATCH i-g-t 06/19] i915/gem_exec_schedule: Verify that using HW semaphores doesn't block Chris Wilson
2019-03-08 18:11 ` [PATCH i-g-t 07/19] i915/gem_exec_nop: poll-sequential requires ordering between rings Chris Wilson
2019-03-08 18:11 ` [PATCH i-g-t 08/19] i915/gem_sync: Make switch-default asymmetric Chris Wilson
2019-03-08 18:11 ` [PATCH i-g-t 09/19] i915/gem_ctx_param: Remove kneecapping Chris Wilson
2019-03-08 18:11 ` [PATCH i-g-t 10/19] i915/gem_exec_big: Add a single shot test Chris Wilson
2019-03-08 18:11 ` [PATCH i-g-t 11/19] kms_fence_pin_leak: Ask for the GPU before use Chris Wilson
2019-03-08 18:11 ` [PATCH i-g-t 12/19] drm-uapi: Import i915_drm.h upto 364df3d04d51 Chris Wilson
2019-03-08 18:11 ` [PATCH i-g-t 13/19] i915: Exercise creating context with shared GTT Chris Wilson
2019-03-08 18:11 ` [PATCH i-g-t 14/19] igt/gem_ctx_switch: Exercise queues Chris Wilson
2019-03-08 18:11 ` [PATCH i-g-t 15/19] igt/gem_exec_whisper: Fork all-engine tests one-per-engine Chris Wilson
2019-03-08 18:11 ` [PATCH i-g-t 16/19] i915/gem_exec_whisper: debugfs/next_seqno is defunct Chris Wilson
2019-03-08 18:11 ` [PATCH i-g-t 17/19] i915: Add gem_ctx_engines Chris Wilson
2019-03-11 10:00   ` Andi Shyti
2019-03-08 18:11 ` [PATCH i-g-t 18/19] i915: Add gem_exec_balancer Chris Wilson
2019-03-12 10:23   ` Tvrtko Ursulin
2019-03-12 10:27     ` Chris Wilson
2019-03-08 18:11 ` [PATCH i-g-t 19/19] i915/gem_exec_balancer: Exercise bonded pairs Chris Wilson
2019-03-11 10:18   ` Andi Shyti
2019-03-11 10:30   ` Andi Shyti
2019-03-11 10:39     ` Chris Wilson
2019-03-09  2:07 ` ✗ Fi.CI.BAT: failure for series starting with [01/19] i915/gem_ppgtt: Estimate resource usage and bail if it means swapping! Patchwork
2019-03-11 10:04 ` [PATCH i-g-t 01/19] " Mika Kuoppala
2019-03-11 10:09   ` 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.