All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH i-g-t 01/25] i915/gem_create: Always try to create an object of at least one page
@ 2019-03-14 14:19 ` Chris Wilson
  0 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev, Matthew Auld

0-byte objects are not allowed.

References: https://bugs.freedesktop.org/show_bug.cgi?id=110106
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Reviewed-by: Matthew Auld <matthew.auld@intel.com>
---
 tests/i915/gem_create.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/i915/gem_create.c b/tests/i915/gem_create.c
index 605b7f9d9..2a861ca8a 100644
--- a/tests/i915/gem_create.c
+++ b/tests/i915/gem_create.c
@@ -149,7 +149,7 @@ static uint64_t get_npages(uint64_t *global, uint64_t npages)
 	max = *global;
 	do {
 		old = max;
-		try = npages % (max / 2);
+		try = 1 + npages % (max / 2);
 		max -= try;
 	} while ((max = __sync_val_compare_and_swap(global, old, max)) != old);
 
-- 
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] 51+ messages in thread

* [igt-dev] [PATCH i-g-t 01/25] i915/gem_create: Always try to create an object of at least one page
@ 2019-03-14 14:19 ` Chris Wilson
  0 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev, Matthew Auld

0-byte objects are not allowed.

References: https://bugs.freedesktop.org/show_bug.cgi?id=110106
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Reviewed-by: Matthew Auld <matthew.auld@intel.com>
---
 tests/i915/gem_create.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/i915/gem_create.c b/tests/i915/gem_create.c
index 605b7f9d9..2a861ca8a 100644
--- a/tests/i915/gem_create.c
+++ b/tests/i915/gem_create.c
@@ -149,7 +149,7 @@ static uint64_t get_npages(uint64_t *global, uint64_t npages)
 	max = *global;
 	do {
 		old = max;
-		try = npages % (max / 2);
+		try = 1 + npages % (max / 2);
 		max -= try;
 	} while ((max = __sync_val_compare_and_swap(global, old, max)) != old);
 
-- 
2.20.1

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

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

* [PATCH i-g-t 02/25] lib/i915: Pretty print HW semaphores
  2019-03-14 14:19 ` [igt-dev] " Chris Wilson
@ 2019-03-14 14:19   ` Chris Wilson
  -1 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

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] 51+ messages in thread

* [igt-dev] [PATCH i-g-t 02/25] lib/i915: Pretty print HW semaphores
@ 2019-03-14 14:19   ` Chris Wilson
  0 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

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

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

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

* [PATCH i-g-t 03/25] lib: Add GPU power measurement
  2019-03-14 14:19 ` [igt-dev] " Chris Wilson
@ 2019-03-14 14:19   ` Chris Wilson
  -1 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

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.am      |   1 +
 lib/Makefile.sources |   2 +
 lib/igt_gpu_power.c  | 114 +++++++++++++++++++++++++++++++++++++++++++
 lib/igt_gpu_power.h  |  59 ++++++++++++++++++++++
 lib/meson.build      |   2 +
 5 files changed, 178 insertions(+)
 create mode 100644 lib/igt_gpu_power.c
 create mode 100644 lib/igt_gpu_power.h

diff --git a/lib/Makefile.am b/lib/Makefile.am
index 3e6a7fdba..62e8bda73 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -84,4 +84,5 @@ libintel_tools_la_LIBADD = \
 	$(LIBUDEV_LIBS) \
 	$(PIXMAN_LIBS) \
 	$(GLIB_LIBS) \
+	libigt_perf.la \
 	-lm
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] 51+ messages in thread

* [igt-dev] [PATCH i-g-t 03/25] lib: Add GPU power measurement
@ 2019-03-14 14:19   ` Chris Wilson
  0 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

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.am      |   1 +
 lib/Makefile.sources |   2 +
 lib/igt_gpu_power.c  | 114 +++++++++++++++++++++++++++++++++++++++++++
 lib/igt_gpu_power.h  |  59 ++++++++++++++++++++++
 lib/meson.build      |   2 +
 5 files changed, 178 insertions(+)
 create mode 100644 lib/igt_gpu_power.c
 create mode 100644 lib/igt_gpu_power.h

diff --git a/lib/Makefile.am b/lib/Makefile.am
index 3e6a7fdba..62e8bda73 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -84,4 +84,5 @@ libintel_tools_la_LIBADD = \
 	$(LIBUDEV_LIBS) \
 	$(PIXMAN_LIBS) \
 	$(GLIB_LIBS) \
+	libigt_perf.la \
 	-lm
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

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

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

* [PATCH i-g-t 04/25] i915/gem_exec_schedule: Measure semaphore power consumption
  2019-03-14 14:19 ` [igt-dev] " Chris Wilson
@ 2019-03-14 14:19   ` Chris Wilson
  -1 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

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] 51+ messages in thread

* [igt-dev] [PATCH i-g-t 04/25] i915/gem_exec_schedule: Measure semaphore power consumption
@ 2019-03-14 14:19   ` Chris Wilson
  0 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

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

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

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

* [PATCH i-g-t 05/25] i915/gem_exec_whisper: Measure total power consumed
  2019-03-14 14:19 ` [igt-dev] " Chris Wilson
@ 2019-03-14 14:19   ` Chris Wilson
  -1 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

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] 51+ messages in thread

* [igt-dev] [PATCH i-g-t 05/25] i915/gem_exec_whisper: Measure total power consumed
@ 2019-03-14 14:19   ` Chris Wilson
  0 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

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

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

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

* [PATCH i-g-t 06/25] i915/gem_exec_schedule: Verify that using HW semaphores doesn't block
  2019-03-14 14:19 ` [igt-dev] " Chris Wilson
@ 2019-03-14 14:19   ` Chris Wilson
  -1 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

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] 51+ messages in thread

* [igt-dev] [PATCH i-g-t 06/25] i915/gem_exec_schedule: Verify that using HW semaphores doesn't block
@ 2019-03-14 14:19   ` Chris Wilson
  0 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

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

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

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

* [PATCH i-g-t 07/25] i915/gem_exec_nop: poll-sequential requires ordering between rings
  2019-03-14 14:19 ` [igt-dev] " Chris Wilson
@ 2019-03-14 14:19   ` Chris Wilson
  -1 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

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] 51+ messages in thread

* [igt-dev] [PATCH i-g-t 07/25] i915/gem_exec_nop: poll-sequential requires ordering between rings
@ 2019-03-14 14:19   ` Chris Wilson
  0 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

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

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

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

* [PATCH i-g-t 08/25] i915/gem_sync: Make switch-default asymmetric
  2019-03-14 14:19 ` [igt-dev] " Chris Wilson
@ 2019-03-14 14:19   ` Chris Wilson
  -1 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

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] 51+ messages in thread

* [igt-dev] [PATCH i-g-t 08/25] i915/gem_sync: Make switch-default asymmetric
@ 2019-03-14 14:19   ` Chris Wilson
  0 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

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

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

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

* [PATCH i-g-t 09/25] i915/gem_ctx_param: Remove kneecapping
  2019-03-14 14:19 ` [igt-dev] " Chris Wilson
@ 2019-03-14 14:19   ` Chris Wilson
  -1 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

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] 51+ messages in thread

* [igt-dev] [PATCH i-g-t 09/25] i915/gem_ctx_param: Remove kneecapping
@ 2019-03-14 14:19   ` Chris Wilson
  0 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

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

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

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

* [PATCH i-g-t 10/25] i915/gem_exec_big: Add a single shot test
  2019-03-14 14:19 ` [igt-dev] " Chris Wilson
@ 2019-03-14 14:19   ` Chris Wilson
  -1 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

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] 51+ messages in thread

* [igt-dev] [PATCH i-g-t 10/25] i915/gem_exec_big: Add a single shot test
@ 2019-03-14 14:19   ` Chris Wilson
  0 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

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

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

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

* [PATCH i-g-t 11/25] kms_fence_pin_leak: Ask for the GPU before use
  2019-03-14 14:19 ` [igt-dev] " Chris Wilson
@ 2019-03-14 14:19   ` Chris Wilson
  -1 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

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] 51+ messages in thread

* [Intel-gfx] [PATCH i-g-t 11/25] kms_fence_pin_leak: Ask for the GPU before use
@ 2019-03-14 14:19   ` Chris Wilson
  0 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

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] 51+ messages in thread

* [PATCH i-g-t 12/25] drm-uapi: Import i915_drm.h upto 364df3d04d51
  2019-03-14 14:19 ` [igt-dev] " Chris Wilson
@ 2019-03-14 14:19   ` Chris Wilson
  -1 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

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 | 372 +++++++++++++++++++++++++++++-------
 1 file changed, 299 insertions(+), 73 deletions(-)

diff --git a/include/drm-uapi/i915_drm.h b/include/drm-uapi/i915_drm.h
index 43fb8ede2..cb6d66178 100644
--- a/include/drm-uapi/i915_drm.h
+++ b/include/drm-uapi/i915_drm.h
@@ -62,6 +62,28 @@ 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;
+	__u32 name;
+	__u32 flags; /* All undefined bits must be zero. */
+	__u32 rsvd[4]; /* Reserved for future use; must be zero. */
+};
+
 /*
  * 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 +121,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 +346,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 +397,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 +408,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 +509,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 +593,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 +616,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 +1015,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 +1121,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 +1172,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 +1466,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 +1518,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 +1587,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 +1618,186 @@ 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:
+ *
+ * Constructed bonded pairs for execution within a virtual engine.
+ *
+ * All engines are equal, but some are more equal than others. Given
+ * the distribution of resources in the HW, it may be preferable to run
+ * a request on a given subset of engines in parallel to a request on a
+ * specific engine. We enable this selection of engines within a virtual
+ * engine by specifying bonding pairs, for any given master engine we will
+ * only execute on one of the corresponding siblings within the virtual engine.
+ *
+ * To execute a request in parallel on the master engine and a sibling requires
+ * coordination with a I915_EXEC_FENCE_SUBMIT.
+ */
+struct i915_context_engines_bond {
+	struct i915_user_extension base;
+
+	__u16 virtual_index; /* index of virtual engine in ctx->engines[] */
+	__u16 mbz;
+
+	__u16 master_class;
+	__u16 master_instance;
+
+	__u64 sibling_mask; /* bitmask of BIT(sibling_index) wrt the v.engine */
+	__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 param;
+};
+
+struct drm_i915_gem_context_create_ext_clone {
+#define I915_CONTEXT_CREATE_EXT_CLONE 1
+	struct i915_user_extension base;
+	__u32 clone_id;
+	__u32 flags;
+#define I915_CONTEXT_CLONE_FLAGS	(1u << 0)
+#define I915_CONTEXT_CLONE_SCHEDATTR	(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 in the outparam @id.
+ *
+ * No flags are defined, with all bits reserved and must be zero.
+ *
+ * An extension chain maybe provided, starting with @extensions, and terminated
+ * by the @next_extension being 0. Currently, no extensions are defined.
+ *
+ * DRM_I915_GEM_VM_DESTROY -
+ *
+ * Destroys a previously created VM id, specified in @id.
+ *
+ * No extensions or flags are allowed currently, and so must be zero.
+ */
+struct drm_i915_gem_vm_control {
+	__u64 extensions;
+	__u32 flags;
+	__u32 vm_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 +1959,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] 51+ messages in thread

* [igt-dev] [PATCH i-g-t 12/25] drm-uapi: Import i915_drm.h upto 364df3d04d51
@ 2019-03-14 14:19   ` Chris Wilson
  0 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

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 | 372 +++++++++++++++++++++++++++++-------
 1 file changed, 299 insertions(+), 73 deletions(-)

diff --git a/include/drm-uapi/i915_drm.h b/include/drm-uapi/i915_drm.h
index 43fb8ede2..cb6d66178 100644
--- a/include/drm-uapi/i915_drm.h
+++ b/include/drm-uapi/i915_drm.h
@@ -62,6 +62,28 @@ 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;
+	__u32 name;
+	__u32 flags; /* All undefined bits must be zero. */
+	__u32 rsvd[4]; /* Reserved for future use; must be zero. */
+};
+
 /*
  * 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 +121,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 +346,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 +397,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 +408,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 +509,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 +593,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 +616,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 +1015,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 +1121,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 +1172,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 +1466,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 +1518,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 +1587,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 +1618,186 @@ 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:
+ *
+ * Constructed bonded pairs for execution within a virtual engine.
+ *
+ * All engines are equal, but some are more equal than others. Given
+ * the distribution of resources in the HW, it may be preferable to run
+ * a request on a given subset of engines in parallel to a request on a
+ * specific engine. We enable this selection of engines within a virtual
+ * engine by specifying bonding pairs, for any given master engine we will
+ * only execute on one of the corresponding siblings within the virtual engine.
+ *
+ * To execute a request in parallel on the master engine and a sibling requires
+ * coordination with a I915_EXEC_FENCE_SUBMIT.
+ */
+struct i915_context_engines_bond {
+	struct i915_user_extension base;
+
+	__u16 virtual_index; /* index of virtual engine in ctx->engines[] */
+	__u16 mbz;
+
+	__u16 master_class;
+	__u16 master_instance;
+
+	__u64 sibling_mask; /* bitmask of BIT(sibling_index) wrt the v.engine */
+	__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 param;
+};
+
+struct drm_i915_gem_context_create_ext_clone {
+#define I915_CONTEXT_CREATE_EXT_CLONE 1
+	struct i915_user_extension base;
+	__u32 clone_id;
+	__u32 flags;
+#define I915_CONTEXT_CLONE_FLAGS	(1u << 0)
+#define I915_CONTEXT_CLONE_SCHEDATTR	(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 in the outparam @id.
+ *
+ * No flags are defined, with all bits reserved and must be zero.
+ *
+ * An extension chain maybe provided, starting with @extensions, and terminated
+ * by the @next_extension being 0. Currently, no extensions are defined.
+ *
+ * DRM_I915_GEM_VM_DESTROY -
+ *
+ * Destroys a previously created VM id, specified in @id.
+ *
+ * No extensions or flags are allowed currently, and so must be zero.
+ */
+struct drm_i915_gem_vm_control {
+	__u64 extensions;
+	__u32 flags;
+	__u32 vm_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 +1959,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

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

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

* [PATCH i-g-t 13/25] lib/i915: Improve gem_context error messages
  2019-03-14 14:19 ` [igt-dev] " Chris Wilson
@ 2019-03-14 14:19   ` Chris Wilson
  -1 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

Avoid embedding the DRM_IOCTL() macro into the error message as it is
unreadable, and instead always wrap the ioctl with a self-descriptive
helper.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 lib/i915/gem_context.c | 72 ++++++++++++++++++++----------------------
 1 file changed, 34 insertions(+), 38 deletions(-)

diff --git a/lib/i915/gem_context.c b/lib/i915/gem_context.c
index 16004685e..8b4d5b704 100644
--- a/lib/i915/gem_context.c
+++ b/lib/i915/gem_context.c
@@ -113,16 +113,16 @@ uint32_t gem_context_create(int fd)
 
 int __gem_context_destroy(int fd, uint32_t ctx_id)
 {
-	struct drm_i915_gem_context_destroy destroy;
-	int ret;
+	struct drm_i915_gem_context_destroy destroy = { ctx_id };
+	int err = 0;
 
-	memset(&destroy, 0, sizeof(destroy));
-	destroy.ctx_id = ctx_id;
+	if (igt_ioctl(fd, DRM_IOCTL_I915_GEM_CONTEXT_DESTROY, &destroy)) {
+		err = -errno;
+		igt_assume(err);
+	}
 
-	ret = igt_ioctl(fd, DRM_IOCTL_I915_GEM_CONTEXT_DESTROY, &destroy);
-	if (ret)
-		return -errno;
-	return 0;
+	errno = 0;
+	return err;
 }
 
 /**
@@ -134,21 +134,20 @@ int __gem_context_destroy(int fd, uint32_t ctx_id)
  */
 void gem_context_destroy(int fd, uint32_t ctx_id)
 {
-	struct drm_i915_gem_context_destroy destroy;
-
-	memset(&destroy, 0, sizeof(destroy));
-	destroy.ctx_id = ctx_id;
-
-	do_ioctl(fd, DRM_IOCTL_I915_GEM_CONTEXT_DESTROY, &destroy);
+	igt_assert_eq(__gem_context_destroy(fd, ctx_id), 0);
 }
 
 int __gem_context_get_param(int fd, struct drm_i915_gem_context_param *p)
 {
-	if (igt_ioctl(fd, DRM_IOCTL_I915_GEM_CONTEXT_GETPARAM, p))
-		return -errno;
+	int err = 0;
+
+	if (igt_ioctl(fd, DRM_IOCTL_I915_GEM_CONTEXT_GETPARAM, p)) {
+		err = -errno;
+		igt_assume(err);
+	}
 
 	errno = 0;
-	return 0;
+	return err;
 }
 
 /**
@@ -161,18 +160,22 @@ int __gem_context_get_param(int fd, struct drm_i915_gem_context_param *p)
  */
 void gem_context_get_param(int fd, struct drm_i915_gem_context_param *p)
 {
-	igt_assert(__gem_context_get_param(fd, p) == 0);
+	igt_assert_eq(__gem_context_get_param(fd, p), 0);
 }
 
-
 int __gem_context_set_param(int fd, struct drm_i915_gem_context_param *p)
 {
-	if (igt_ioctl(fd, DRM_IOCTL_I915_GEM_CONTEXT_SETPARAM, p))
-		return -errno;
+	int err = 0;
+
+	if (igt_ioctl(fd, DRM_IOCTL_I915_GEM_CONTEXT_SETPARAM, p)) {
+		err = -errno;
+		igt_assume(err);
+	}
 
 	errno = 0;
-	return 0;
+	return err;
 }
+
 /**
  * gem_context_set_param:
  * @fd: open i915 drm file descriptor
@@ -183,7 +186,7 @@ int __gem_context_set_param(int fd, struct drm_i915_gem_context_param *p)
  */
 void gem_context_set_param(int fd, struct drm_i915_gem_context_param *p)
 {
-	igt_assert(__gem_context_set_param(fd, p) == 0);
+	igt_assert_eq(__gem_context_set_param(fd, p), 0);
 }
 
 /**
@@ -196,14 +199,9 @@ void gem_context_set_param(int fd, struct drm_i915_gem_context_param *p)
  */
 void gem_context_require_param(int fd, uint64_t param)
 {
-	struct drm_i915_gem_context_param p;
-
-	p.ctx_id = 0;
-	p.param = param;
-	p.value = 0;
-	p.size = 0;
+	struct drm_i915_gem_context_param p = { .param = param };
 
-	igt_require(igt_ioctl(fd, DRM_IOCTL_I915_GEM_CONTEXT_GETPARAM, &p) == 0);
+	igt_require(__gem_context_get_param(fd, &p) == 0);
 }
 
 void gem_context_require_bannable(int fd)
@@ -252,13 +250,11 @@ void gem_context_require_bannable(int fd)
  */
 int __gem_context_set_priority(int fd, uint32_t ctx_id, int prio)
 {
-	struct drm_i915_gem_context_param p;
-
-	memset(&p, 0, sizeof(p));
-	p.ctx_id = ctx_id;
-	p.size = 0;
-	p.param = DRM_I915_CONTEXT_PARAM_PRIORITY;
-	p.value = prio;
+	struct drm_i915_gem_context_param p = {
+		.ctx_id = ctx_id,
+		.param = DRM_I915_CONTEXT_PARAM_PRIORITY,
+		.value = prio,
+	};
 
 	return __gem_context_set_param(fd, &p);
 }
@@ -273,5 +269,5 @@ int __gem_context_set_priority(int fd, uint32_t ctx_id, int prio)
  */
 void gem_context_set_priority(int fd, uint32_t ctx_id, int prio)
 {
-	igt_assert(__gem_context_set_priority(fd, ctx_id, prio) == 0);
+	igt_assert_eq(__gem_context_set_priority(fd, ctx_id, prio), 0);
 }
-- 
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] 51+ messages in thread

* [igt-dev] [PATCH i-g-t 13/25] lib/i915: Improve gem_context error messages
@ 2019-03-14 14:19   ` Chris Wilson
  0 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

Avoid embedding the DRM_IOCTL() macro into the error message as it is
unreadable, and instead always wrap the ioctl with a self-descriptive
helper.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 lib/i915/gem_context.c | 72 ++++++++++++++++++++----------------------
 1 file changed, 34 insertions(+), 38 deletions(-)

diff --git a/lib/i915/gem_context.c b/lib/i915/gem_context.c
index 16004685e..8b4d5b704 100644
--- a/lib/i915/gem_context.c
+++ b/lib/i915/gem_context.c
@@ -113,16 +113,16 @@ uint32_t gem_context_create(int fd)
 
 int __gem_context_destroy(int fd, uint32_t ctx_id)
 {
-	struct drm_i915_gem_context_destroy destroy;
-	int ret;
+	struct drm_i915_gem_context_destroy destroy = { ctx_id };
+	int err = 0;
 
-	memset(&destroy, 0, sizeof(destroy));
-	destroy.ctx_id = ctx_id;
+	if (igt_ioctl(fd, DRM_IOCTL_I915_GEM_CONTEXT_DESTROY, &destroy)) {
+		err = -errno;
+		igt_assume(err);
+	}
 
-	ret = igt_ioctl(fd, DRM_IOCTL_I915_GEM_CONTEXT_DESTROY, &destroy);
-	if (ret)
-		return -errno;
-	return 0;
+	errno = 0;
+	return err;
 }
 
 /**
@@ -134,21 +134,20 @@ int __gem_context_destroy(int fd, uint32_t ctx_id)
  */
 void gem_context_destroy(int fd, uint32_t ctx_id)
 {
-	struct drm_i915_gem_context_destroy destroy;
-
-	memset(&destroy, 0, sizeof(destroy));
-	destroy.ctx_id = ctx_id;
-
-	do_ioctl(fd, DRM_IOCTL_I915_GEM_CONTEXT_DESTROY, &destroy);
+	igt_assert_eq(__gem_context_destroy(fd, ctx_id), 0);
 }
 
 int __gem_context_get_param(int fd, struct drm_i915_gem_context_param *p)
 {
-	if (igt_ioctl(fd, DRM_IOCTL_I915_GEM_CONTEXT_GETPARAM, p))
-		return -errno;
+	int err = 0;
+
+	if (igt_ioctl(fd, DRM_IOCTL_I915_GEM_CONTEXT_GETPARAM, p)) {
+		err = -errno;
+		igt_assume(err);
+	}
 
 	errno = 0;
-	return 0;
+	return err;
 }
 
 /**
@@ -161,18 +160,22 @@ int __gem_context_get_param(int fd, struct drm_i915_gem_context_param *p)
  */
 void gem_context_get_param(int fd, struct drm_i915_gem_context_param *p)
 {
-	igt_assert(__gem_context_get_param(fd, p) == 0);
+	igt_assert_eq(__gem_context_get_param(fd, p), 0);
 }
 
-
 int __gem_context_set_param(int fd, struct drm_i915_gem_context_param *p)
 {
-	if (igt_ioctl(fd, DRM_IOCTL_I915_GEM_CONTEXT_SETPARAM, p))
-		return -errno;
+	int err = 0;
+
+	if (igt_ioctl(fd, DRM_IOCTL_I915_GEM_CONTEXT_SETPARAM, p)) {
+		err = -errno;
+		igt_assume(err);
+	}
 
 	errno = 0;
-	return 0;
+	return err;
 }
+
 /**
  * gem_context_set_param:
  * @fd: open i915 drm file descriptor
@@ -183,7 +186,7 @@ int __gem_context_set_param(int fd, struct drm_i915_gem_context_param *p)
  */
 void gem_context_set_param(int fd, struct drm_i915_gem_context_param *p)
 {
-	igt_assert(__gem_context_set_param(fd, p) == 0);
+	igt_assert_eq(__gem_context_set_param(fd, p), 0);
 }
 
 /**
@@ -196,14 +199,9 @@ void gem_context_set_param(int fd, struct drm_i915_gem_context_param *p)
  */
 void gem_context_require_param(int fd, uint64_t param)
 {
-	struct drm_i915_gem_context_param p;
-
-	p.ctx_id = 0;
-	p.param = param;
-	p.value = 0;
-	p.size = 0;
+	struct drm_i915_gem_context_param p = { .param = param };
 
-	igt_require(igt_ioctl(fd, DRM_IOCTL_I915_GEM_CONTEXT_GETPARAM, &p) == 0);
+	igt_require(__gem_context_get_param(fd, &p) == 0);
 }
 
 void gem_context_require_bannable(int fd)
@@ -252,13 +250,11 @@ void gem_context_require_bannable(int fd)
  */
 int __gem_context_set_priority(int fd, uint32_t ctx_id, int prio)
 {
-	struct drm_i915_gem_context_param p;
-
-	memset(&p, 0, sizeof(p));
-	p.ctx_id = ctx_id;
-	p.size = 0;
-	p.param = DRM_I915_CONTEXT_PARAM_PRIORITY;
-	p.value = prio;
+	struct drm_i915_gem_context_param p = {
+		.ctx_id = ctx_id,
+		.param = DRM_I915_CONTEXT_PARAM_PRIORITY,
+		.value = prio,
+	};
 
 	return __gem_context_set_param(fd, &p);
 }
@@ -273,5 +269,5 @@ int __gem_context_set_priority(int fd, uint32_t ctx_id, int prio)
  */
 void gem_context_set_priority(int fd, uint32_t ctx_id, int prio)
 {
-	igt_assert(__gem_context_set_priority(fd, ctx_id, prio) == 0);
+	igt_assert_eq(__gem_context_set_priority(fd, ctx_id, prio), 0);
 }
-- 
2.20.1

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

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

* [PATCH i-g-t 14/25] i915/gem_ctx_param: Test set/get (copy) VM
  2019-03-14 14:19 ` [igt-dev] " Chris Wilson
@ 2019-03-14 14:19   ` Chris Wilson
  -1 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

Exercise reusing the GTT of one ctx in another.

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

diff --git a/tests/i915/gem_ctx_param.c b/tests/i915/gem_ctx_param.c
index b3f8637df..54ade8b4b 100644
--- a/tests/i915/gem_ctx_param.c
+++ b/tests/i915/gem_ctx_param.c
@@ -36,17 +36,6 @@ IGT_TEST_DESCRIPTION("Basic test for context set/get param input validation.");
 #define NEW_CTX	BIT(0)
 #define USER BIT(1)
 
-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 set_priority(int i915)
 {
 	static const int64_t test_values[] = {
@@ -91,7 +80,7 @@ static void set_priority(int i915)
 	igt_permute_array(values, size, igt_exchange_int64);
 
 	igt_fork(flags, NEW_CTX | USER) {
-		int fd = reopen_driver(i915);
+		int fd = gem_reopen_driver(i915);
 		struct drm_i915_gem_context_param arg = {
 			.param = I915_CONTEXT_PARAM_PRIORITY,
 			.ctx_id = flags & NEW_CTX ? gem_context_create(fd) : 0,
@@ -143,6 +132,73 @@ static void set_priority(int i915)
 	free(values);
 }
 
+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 test_vm(int i915)
+{
+	struct drm_i915_gem_exec_object2 batch = {
+		.handle = batch_create(i915),
+	};
+	struct drm_i915_gem_execbuffer2 eb = {
+		.buffers_ptr = to_user_pointer(&batch),
+		.buffer_count = 1,
+	};
+	struct drm_i915_gem_context_param arg = {
+		.param = I915_CONTEXT_PARAM_VM,
+	};
+	uint32_t parent, child;
+
+	arg.value = -1ull;
+	igt_require(__gem_context_set_param(i915, &arg) == -ENOENT);
+
+	parent = gem_context_create(i915);
+	child = gem_context_create(i915);
+
+	eb.rsvd1 = parent;
+	batch.offset = 48 << 20;
+	gem_execbuf(i915, &eb);
+	igt_assert_eq_u64(batch.offset, 48 << 20);
+
+	eb.rsvd1 = child;
+	batch.offset = 0;
+	gem_execbuf(i915, &eb);
+	igt_assert_eq_u64(batch.offset, 0);
+
+	eb.rsvd1 = parent;
+	gem_execbuf(i915, &eb);
+	igt_assert_eq_u64(batch.offset, 48 << 20);
+
+	arg.ctx_id = parent;
+	gem_context_get_param(i915, &arg);
+
+	arg.ctx_id = child;
+	gem_context_set_param(i915, &arg);
+
+	eb.rsvd1 = child;
+	gem_execbuf(i915, &eb);
+	igt_assert_eq_u64(batch.offset, 48 << 20);
+
+	gem_context_destroy(i915, child);
+	gem_context_destroy(i915, parent);
+
+	gem_sync(i915, batch.handle);
+	gem_close(i915, batch.handle);
+}
+
 igt_main
 {
 	struct drm_i915_gem_context_param arg;
@@ -253,6 +309,9 @@ igt_main
 		gem_context_set_param(fd, &arg);
 	}
 
+	igt_subtest("vm")
+		test_vm(fd);
+
 	arg.param = I915_CONTEXT_PARAM_PRIORITY;
 
 	igt_subtest("set-priority-not-supported") {
-- 
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] 51+ messages in thread

* [igt-dev] [PATCH i-g-t 14/25] i915/gem_ctx_param: Test set/get (copy) VM
@ 2019-03-14 14:19   ` Chris Wilson
  0 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

Exercise reusing the GTT of one ctx in another.

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

diff --git a/tests/i915/gem_ctx_param.c b/tests/i915/gem_ctx_param.c
index b3f8637df..54ade8b4b 100644
--- a/tests/i915/gem_ctx_param.c
+++ b/tests/i915/gem_ctx_param.c
@@ -36,17 +36,6 @@ IGT_TEST_DESCRIPTION("Basic test for context set/get param input validation.");
 #define NEW_CTX	BIT(0)
 #define USER BIT(1)
 
-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 set_priority(int i915)
 {
 	static const int64_t test_values[] = {
@@ -91,7 +80,7 @@ static void set_priority(int i915)
 	igt_permute_array(values, size, igt_exchange_int64);
 
 	igt_fork(flags, NEW_CTX | USER) {
-		int fd = reopen_driver(i915);
+		int fd = gem_reopen_driver(i915);
 		struct drm_i915_gem_context_param arg = {
 			.param = I915_CONTEXT_PARAM_PRIORITY,
 			.ctx_id = flags & NEW_CTX ? gem_context_create(fd) : 0,
@@ -143,6 +132,73 @@ static void set_priority(int i915)
 	free(values);
 }
 
+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 test_vm(int i915)
+{
+	struct drm_i915_gem_exec_object2 batch = {
+		.handle = batch_create(i915),
+	};
+	struct drm_i915_gem_execbuffer2 eb = {
+		.buffers_ptr = to_user_pointer(&batch),
+		.buffer_count = 1,
+	};
+	struct drm_i915_gem_context_param arg = {
+		.param = I915_CONTEXT_PARAM_VM,
+	};
+	uint32_t parent, child;
+
+	arg.value = -1ull;
+	igt_require(__gem_context_set_param(i915, &arg) == -ENOENT);
+
+	parent = gem_context_create(i915);
+	child = gem_context_create(i915);
+
+	eb.rsvd1 = parent;
+	batch.offset = 48 << 20;
+	gem_execbuf(i915, &eb);
+	igt_assert_eq_u64(batch.offset, 48 << 20);
+
+	eb.rsvd1 = child;
+	batch.offset = 0;
+	gem_execbuf(i915, &eb);
+	igt_assert_eq_u64(batch.offset, 0);
+
+	eb.rsvd1 = parent;
+	gem_execbuf(i915, &eb);
+	igt_assert_eq_u64(batch.offset, 48 << 20);
+
+	arg.ctx_id = parent;
+	gem_context_get_param(i915, &arg);
+
+	arg.ctx_id = child;
+	gem_context_set_param(i915, &arg);
+
+	eb.rsvd1 = child;
+	gem_execbuf(i915, &eb);
+	igt_assert_eq_u64(batch.offset, 48 << 20);
+
+	gem_context_destroy(i915, child);
+	gem_context_destroy(i915, parent);
+
+	gem_sync(i915, batch.handle);
+	gem_close(i915, batch.handle);
+}
+
 igt_main
 {
 	struct drm_i915_gem_context_param arg;
@@ -253,6 +309,9 @@ igt_main
 		gem_context_set_param(fd, &arg);
 	}
 
+	igt_subtest("vm")
+		test_vm(fd);
+
 	arg.param = I915_CONTEXT_PARAM_PRIORITY;
 
 	igt_subtest("set-priority-not-supported") {
-- 
2.20.1

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

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

* [PATCH i-g-t 15/25] i915/gem_ctx_create: Basic checks for constructor properties
  2019-03-14 14:19 ` [igt-dev] " Chris Wilson
@ 2019-03-14 14:19   ` Chris Wilson
  -1 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

Check that the extended create interface accepts setparam.

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

diff --git a/tests/i915/gem_ctx_create.c b/tests/i915/gem_ctx_create.c
index a664070db..e12f41691 100644
--- a/tests/i915/gem_ctx_create.c
+++ b/tests/i915/gem_ctx_create.c
@@ -308,6 +308,60 @@ static void maximum(int fd, int ncpus, unsigned mode)
 	free(contexts);
 }
 
+static int __create_ext(int i915, struct drm_i915_gem_context_create_ext *arg)
+{
+	int err;
+
+	err = 0;
+	if (igt_ioctl(i915, DRM_IOCTL_I915_GEM_CONTEXT_CREATE_EXT, arg))
+		err = -errno;
+
+	errno = 0;
+	return err;
+}
+
+static void basic_ext_param(int i915)
+{
+	struct drm_i915_gem_context_create_ext_setparam ext = {
+		{ .name = I915_CONTEXT_CREATE_EXT_SETPARAM },
+	};
+	struct drm_i915_gem_context_create_ext create = {
+		.flags = I915_CONTEXT_CREATE_FLAGS_USE_EXTENSIONS
+	};
+	struct drm_i915_gem_context_param get;
+
+	igt_require(__create_ext(i915, &create) == 0);
+	gem_context_destroy(i915, create.ctx_id);
+
+	create.extensions = -1ull;
+	igt_assert_eq(__create_ext(i915, &create), -EFAULT);
+
+	create.extensions = to_user_pointer(&ext);
+	igt_assert_eq(__create_ext(i915, &create), -EINVAL);
+
+	ext.param.param = I915_CONTEXT_PARAM_PRIORITY;
+	if (__create_ext(i915, &create) != -ENODEV) {
+		gem_context_destroy(i915, create.ctx_id);
+
+		ext.base.next_extension = -1ull;
+		igt_assert_eq(__create_ext(i915, &create), -EFAULT);
+		ext.base.next_extension = to_user_pointer(&ext);
+		igt_assert_eq(__create_ext(i915, &create), -E2BIG);
+		ext.base.next_extension = 0;
+
+		ext.param.value = 32;
+		igt_assert_eq(__create_ext(i915, &create), 0);
+
+		memset(&get, 0, sizeof(get));
+		get.ctx_id = create.ctx_id;
+		get.param = I915_CONTEXT_PARAM_PRIORITY;
+		gem_context_get_param(i915, &get);
+		igt_assert_eq(get.value, ext.param.value);
+
+		gem_context_destroy(i915, create.ctx_id);
+	}
+}
+
 igt_main
 {
 	const int ncpus = sysconf(_SC_NPROCESSORS_ONLN);
@@ -352,6 +406,9 @@ igt_main
 		igt_assert_eq(__gem_context_create_local(fd, &create), -EINVAL);
 	}
 
+	igt_subtest("ext-param")
+		basic_ext_param(fd);
+
 	igt_subtest("maximum-mem")
 		maximum(fd, ncpus, CHECK_RAM);
 	igt_subtest("maximum-swap")
-- 
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] 51+ messages in thread

* [igt-dev] [PATCH i-g-t 15/25] i915/gem_ctx_create: Basic checks for constructor properties
@ 2019-03-14 14:19   ` Chris Wilson
  0 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

Check that the extended create interface accepts setparam.

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

diff --git a/tests/i915/gem_ctx_create.c b/tests/i915/gem_ctx_create.c
index a664070db..e12f41691 100644
--- a/tests/i915/gem_ctx_create.c
+++ b/tests/i915/gem_ctx_create.c
@@ -308,6 +308,60 @@ static void maximum(int fd, int ncpus, unsigned mode)
 	free(contexts);
 }
 
+static int __create_ext(int i915, struct drm_i915_gem_context_create_ext *arg)
+{
+	int err;
+
+	err = 0;
+	if (igt_ioctl(i915, DRM_IOCTL_I915_GEM_CONTEXT_CREATE_EXT, arg))
+		err = -errno;
+
+	errno = 0;
+	return err;
+}
+
+static void basic_ext_param(int i915)
+{
+	struct drm_i915_gem_context_create_ext_setparam ext = {
+		{ .name = I915_CONTEXT_CREATE_EXT_SETPARAM },
+	};
+	struct drm_i915_gem_context_create_ext create = {
+		.flags = I915_CONTEXT_CREATE_FLAGS_USE_EXTENSIONS
+	};
+	struct drm_i915_gem_context_param get;
+
+	igt_require(__create_ext(i915, &create) == 0);
+	gem_context_destroy(i915, create.ctx_id);
+
+	create.extensions = -1ull;
+	igt_assert_eq(__create_ext(i915, &create), -EFAULT);
+
+	create.extensions = to_user_pointer(&ext);
+	igt_assert_eq(__create_ext(i915, &create), -EINVAL);
+
+	ext.param.param = I915_CONTEXT_PARAM_PRIORITY;
+	if (__create_ext(i915, &create) != -ENODEV) {
+		gem_context_destroy(i915, create.ctx_id);
+
+		ext.base.next_extension = -1ull;
+		igt_assert_eq(__create_ext(i915, &create), -EFAULT);
+		ext.base.next_extension = to_user_pointer(&ext);
+		igt_assert_eq(__create_ext(i915, &create), -E2BIG);
+		ext.base.next_extension = 0;
+
+		ext.param.value = 32;
+		igt_assert_eq(__create_ext(i915, &create), 0);
+
+		memset(&get, 0, sizeof(get));
+		get.ctx_id = create.ctx_id;
+		get.param = I915_CONTEXT_PARAM_PRIORITY;
+		gem_context_get_param(i915, &get);
+		igt_assert_eq(get.value, ext.param.value);
+
+		gem_context_destroy(i915, create.ctx_id);
+	}
+}
+
 igt_main
 {
 	const int ncpus = sysconf(_SC_NPROCESSORS_ONLN);
@@ -352,6 +406,9 @@ igt_main
 		igt_assert_eq(__gem_context_create_local(fd, &create), -EINVAL);
 	}
 
+	igt_subtest("ext-param")
+		basic_ext_param(fd);
+
 	igt_subtest("maximum-mem")
 		maximum(fd, ncpus, CHECK_RAM);
 	igt_subtest("maximum-swap")
-- 
2.20.1

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

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

* [PATCH i-g-t 16/25] i915: Add gem_ctx_clone
  2019-03-14 14:19 ` [igt-dev] " Chris Wilson
@ 2019-03-14 14:19   ` Chris Wilson
  -1 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

Exercise cloning contexts, an extension of merely creating one.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 tests/Makefile.sources     |   1 +
 tests/i915/gem_ctx_clone.c | 421 +++++++++++++++++++++++++++++++++++++
 tests/meson.build          |   1 +
 3 files changed, 423 insertions(+)
 create mode 100644 tests/i915/gem_ctx_clone.c

diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index 71ccf00af..6d0fe1c95 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_clone \
 	kms_3d \
 	kms_addfb_basic \
 	kms_atomic \
diff --git a/tests/i915/gem_ctx_clone.c b/tests/i915/gem_ctx_clone.c
new file mode 100644
index 000000000..7d83c6350
--- /dev/null
+++ b/tests/i915/gem_ctx_clone.c
@@ -0,0 +1,421 @@
+/*
+ * 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.
+ */
+
+#include "igt.h"
+#include "i915/gem_vm.h"
+#include "i915_drm.h"
+
+static int __create_ext(int i915, struct drm_i915_gem_context_create_ext *arg)
+{
+	int err;
+
+	err = 0;
+	if (igt_ioctl(i915, DRM_IOCTL_I915_GEM_CONTEXT_CREATE_EXT, arg))
+		err = -errno;
+
+	errno = 0;
+	return err;
+}
+
+static bool has_ctx_clone(int i915)
+{
+	struct drm_i915_gem_context_create_ext_clone ext = {
+		{ .name = I915_CONTEXT_CREATE_EXT_CLONE },
+		.clone_id = -1,
+	};
+	struct drm_i915_gem_context_create_ext create = {
+		.flags = I915_CONTEXT_CREATE_FLAGS_USE_EXTENSIONS,
+		.extensions = to_user_pointer(&ext),
+	};
+	return __create_ext(i915, &create) == -ENOENT;
+}
+
+static void invalid_clone(int i915)
+{
+	struct drm_i915_gem_context_create_ext_clone ext = {
+		{ .name = I915_CONTEXT_CREATE_EXT_CLONE },
+	};
+	struct drm_i915_gem_context_create_ext create = {
+		.flags = I915_CONTEXT_CREATE_FLAGS_USE_EXTENSIONS,
+		.extensions = to_user_pointer(&ext),
+	};
+
+	igt_assert_eq(__create_ext(i915, &create), 0);
+	gem_context_destroy(i915, create.ctx_id);
+
+	ext.flags = -1; /* Hopefully we won't run out of flags */
+	igt_assert_eq(__create_ext(i915, &create), -EINVAL);
+	ext.flags = 0;
+
+	ext.base.next_extension = -1;
+	igt_assert_eq(__create_ext(i915, &create), -EFAULT);
+	ext.base.next_extension = to_user_pointer(&ext);
+	igt_assert_eq(__create_ext(i915, &create), -E2BIG);
+	ext.base.next_extension = 0;
+
+	ext.clone_id = -1;
+	igt_assert_eq(__create_ext(i915, &create), -ENOENT);
+	ext.clone_id = 0;
+}
+
+static void clone_flags(int i915)
+{
+	struct drm_i915_gem_context_create_ext_setparam set = {
+		{ .name = I915_CONTEXT_CREATE_EXT_SETPARAM },
+		{ .param = I915_CONTEXT_PARAM_RECOVERABLE },
+	};
+	struct drm_i915_gem_context_create_ext_clone ext = {
+		{ .name = I915_CONTEXT_CREATE_EXT_CLONE },
+		.flags = I915_CONTEXT_CLONE_FLAGS,
+	};
+	struct drm_i915_gem_context_create_ext create = {
+		.flags = I915_CONTEXT_CREATE_FLAGS_USE_EXTENSIONS,
+		.extensions = to_user_pointer(&ext),
+	};
+	int expected;
+
+	set.param.value = 1; /* default is recoverable */
+	igt_require(__gem_context_set_param(i915, &set.param) == 0);
+
+	for (int pass = 0; pass < 2; pass++) { /* cloning default, then child */
+		igt_debug("Cloning %d\n", ext.clone_id);
+		igt_assert_eq(__create_ext(i915, &create), 0);
+
+		set.param.ctx_id = ext.clone_id;
+		gem_context_get_param(i915, &set.param);
+		expected = set.param.value;
+
+		set.param.ctx_id = create.ctx_id;
+		gem_context_get_param(i915, &set.param);
+
+		igt_assert_eq_u64(set.param.param,
+				  I915_CONTEXT_PARAM_RECOVERABLE);
+		igt_assert_eq((int)set.param.value, expected);
+
+		gem_context_destroy(i915, create.ctx_id);
+
+		expected = set.param.value = 0;
+		set.param.ctx_id = ext.clone_id;
+		gem_context_set_param(i915, &set.param);
+
+		igt_assert_eq(__create_ext(i915, &create), 0);
+
+		set.param.ctx_id = create.ctx_id;
+		gem_context_get_param(i915, &set.param);
+
+		igt_assert_eq_u64(set.param.param,
+				  I915_CONTEXT_PARAM_RECOVERABLE);
+		igt_assert_eq((int)set.param.value, expected);
+
+		gem_context_destroy(i915, create.ctx_id);
+
+		/* clone but then reset priority to default */
+		set.param.ctx_id = 0;
+		set.param.value = 1;
+		ext.base.next_extension = to_user_pointer(&set);
+		igt_assert_eq(__create_ext(i915, &create), 0);
+		ext.base.next_extension = 0;
+
+		set.param.ctx_id = create.ctx_id;
+		gem_context_get_param(i915, &set.param);
+		igt_assert_eq_u64(set.param.value, 1);
+
+		set.param.ctx_id = ext.clone_id;
+		gem_context_get_param(i915, &set.param);
+		igt_assert_eq_u64(set.param.value, 0);
+
+		gem_context_destroy(i915, create.ctx_id);
+		ext.clone_id = gem_context_create(i915);
+	}
+
+	gem_context_destroy(i915, ext.clone_id);
+}
+
+static void clone_engines(int i915)
+{
+	struct drm_i915_gem_context_create_ext_setparam set = {
+		{ .name = I915_CONTEXT_CREATE_EXT_SETPARAM },
+		{ .param = I915_CONTEXT_PARAM_ENGINES },
+	};
+	struct drm_i915_gem_context_create_ext_clone ext = {
+		{ .name = I915_CONTEXT_CREATE_EXT_CLONE },
+		.flags = I915_CONTEXT_CLONE_ENGINES,
+	};
+	struct drm_i915_gem_context_create_ext create = {
+		.flags = I915_CONTEXT_CREATE_FLAGS_USE_EXTENSIONS,
+		.extensions = to_user_pointer(&ext),
+	};
+	I915_DEFINE_CONTEXT_PARAM_ENGINES(expected, 64);
+	I915_DEFINE_CONTEXT_PARAM_ENGINES(engines, 64);
+	uint64_t ex_size;
+
+	memset(&expected, 0, sizeof(expected));
+	memset(&engines, 0, sizeof(engines));
+
+	igt_require(__gem_context_set_param(i915, &set.param) == 0);
+
+	for (int pass = 0; pass < 2; pass++) { /* cloning default, then child */
+		igt_debug("Cloning %d\n", ext.clone_id);
+		igt_assert_eq(__create_ext(i915, &create), 0);
+
+		set.param.ctx_id = ext.clone_id;
+		set.param.size = sizeof(expected);
+		set.param.value = to_user_pointer(&expected);
+		gem_context_get_param(i915, &set.param);
+		ex_size = set.param.size;
+
+		set.param.ctx_id = create.ctx_id;
+		set.param.size = sizeof(engines);
+		set.param.value = to_user_pointer(&engines);
+		gem_context_get_param(i915, &set.param);
+
+		igt_assert_eq_u64(set.param.param, I915_CONTEXT_PARAM_ENGINES);
+		igt_assert_eq_u64(set.param.size, ex_size);
+		igt_assert(!memcmp(&engines, &expected, ex_size));
+
+		gem_context_destroy(i915, create.ctx_id);
+
+		expected.class_instance[0].engine_class =
+			I915_ENGINE_CLASS_INVALID;
+		expected.class_instance[0].engine_instance =
+			I915_ENGINE_CLASS_INVALID_NONE;
+		ex_size = (sizeof(struct i915_context_param_engines) +
+			   sizeof(expected.class_instance[0]));
+
+		set.param.ctx_id = ext.clone_id;
+		set.param.size = ex_size;
+		set.param.value = to_user_pointer(&expected);
+		gem_context_set_param(i915, &set.param);
+
+		igt_assert_eq(__create_ext(i915, &create), 0);
+
+		set.param.ctx_id = create.ctx_id;
+		set.param.size = sizeof(engines);
+		set.param.value = to_user_pointer(&engines);
+		gem_context_get_param(i915, &set.param);
+
+		igt_assert_eq_u64(set.param.size, ex_size);
+		igt_assert(!memcmp(&engines, &expected, ex_size));
+
+		gem_context_destroy(i915, create.ctx_id);
+
+		/* clone but then reset engines to default */
+		set.param.ctx_id = 0;
+		set.param.size = 0;
+		set.param.value = 0;
+		ext.base.next_extension = to_user_pointer(&set);
+
+		igt_assert_eq(__create_ext(i915, &create), 0);
+		ext.base.next_extension = 0;
+
+		set.param.ctx_id = create.ctx_id;
+		set.param.size = sizeof(engines);
+		set.param.value = to_user_pointer(&engines);
+		gem_context_get_param(i915, &set.param);
+		igt_assert_eq_u64(set.param.size, 0);
+
+		gem_context_destroy(i915, create.ctx_id);
+
+		/* And check we ignore the flag */
+		ext.flags = 0;
+		igt_assert_eq(__create_ext(i915, &create), 0);
+		ext.flags = I915_CONTEXT_CLONE_ENGINES;
+
+		set.param.ctx_id = create.ctx_id;
+		set.param.size = sizeof(engines);
+		set.param.value = to_user_pointer(&engines);
+		gem_context_get_param(i915, &set.param);
+		igt_assert_eq_u64(set.param.size, 0);
+
+		ext.clone_id = gem_context_create(i915);
+	}
+
+	gem_context_destroy(i915, ext.clone_id);
+}
+
+static void clone_scheduler(int i915)
+{
+	struct drm_i915_gem_context_create_ext_setparam set = {
+		{ .name = I915_CONTEXT_CREATE_EXT_SETPARAM },
+		{ .param = I915_CONTEXT_PARAM_PRIORITY },
+	};
+	struct drm_i915_gem_context_create_ext_clone ext = {
+		{ .name = I915_CONTEXT_CREATE_EXT_CLONE },
+		.flags = I915_CONTEXT_CLONE_SCHEDATTR,
+	};
+	struct drm_i915_gem_context_create_ext create = {
+		.flags = I915_CONTEXT_CREATE_FLAGS_USE_EXTENSIONS,
+		.extensions = to_user_pointer(&ext),
+	};
+	int expected;
+
+	igt_require(__gem_context_set_param(i915, &set.param) == 0);
+
+	for (int pass = 0; pass < 2; pass++) { /* cloning default, then child */
+		igt_debug("Cloning %d\n", ext.clone_id);
+		igt_assert_eq(__create_ext(i915, &create), 0);
+
+		set.param.ctx_id = ext.clone_id;
+		gem_context_get_param(i915, &set.param);
+		expected = set.param.value;
+
+		set.param.ctx_id = create.ctx_id;
+		gem_context_get_param(i915, &set.param);
+
+		igt_assert_eq_u64(set.param.param, I915_CONTEXT_PARAM_PRIORITY);
+		igt_assert_eq((int)set.param.value, expected);
+
+		gem_context_destroy(i915, create.ctx_id);
+
+		expected = set.param.value = 1;
+		set.param.ctx_id = ext.clone_id;
+		gem_context_set_param(i915, &set.param);
+
+		igt_assert_eq(__create_ext(i915, &create), 0);
+
+		set.param.ctx_id = create.ctx_id;
+		gem_context_get_param(i915, &set.param);
+
+		igt_assert_eq_u64(set.param.param, I915_CONTEXT_PARAM_PRIORITY);
+		igt_assert_eq((int)set.param.value, expected);
+
+		gem_context_destroy(i915, create.ctx_id);
+
+		/* clone but then reset priority to default */
+		set.param.ctx_id = 0;
+		set.param.value = 0;
+		ext.base.next_extension = to_user_pointer(&set);
+		igt_assert_eq(__create_ext(i915, &create), 0);
+		ext.base.next_extension = 0;
+
+		set.param.ctx_id = create.ctx_id;
+		gem_context_get_param(i915, &set.param);
+		igt_assert_eq_u64(set.param.value, 0);
+
+		set.param.ctx_id = ext.clone_id;
+		gem_context_get_param(i915, &set.param);
+		igt_assert_eq_u64(set.param.value, 1);
+
+		gem_context_destroy(i915, create.ctx_id);
+		ext.clone_id = gem_context_create(i915);
+	}
+
+	gem_context_destroy(i915, ext.clone_id);
+}
+
+static void clone_vm(int i915)
+{
+	struct drm_i915_gem_context_create_ext_setparam set = {
+		{ .name = I915_CONTEXT_CREATE_EXT_SETPARAM },
+		{ .param = I915_CONTEXT_PARAM_VM },
+	};
+	struct drm_i915_gem_context_create_ext_clone ext = {
+		{ .name = I915_CONTEXT_CREATE_EXT_CLONE },
+		.flags = I915_CONTEXT_CLONE_VM,
+	};
+	struct drm_i915_gem_context_create_ext create = {
+		.flags = I915_CONTEXT_CREATE_FLAGS_USE_EXTENSIONS,
+		.extensions = to_user_pointer(&ext),
+	};
+	uint32_t vm_id[2];
+	int expected;
+
+	igt_require(__gem_context_set_param(i915, &set.param) == -ENOENT);
+
+	set.param.ctx_id = gem_context_create(i915);
+	gem_context_get_param(i915, &set.param);
+	vm_id[0] = set.param.value;
+	gem_context_destroy(i915, set.param.ctx_id);
+	set.param.ctx_id = 0;
+
+	vm_id[1] = gem_vm_create(i915);
+
+	for (int pass = 0; pass < 2; pass++) { /* cloning default, then child */
+		igt_debug("Cloning %d\n", ext.clone_id);
+		igt_assert_eq(__create_ext(i915, &create), 0);
+
+		set.param.ctx_id = ext.clone_id;
+		gem_context_get_param(i915, &set.param);
+		expected = set.param.value;
+
+		set.param.ctx_id = create.ctx_id;
+		gem_context_get_param(i915, &set.param);
+
+		igt_assert_eq_u64(set.param.param, I915_CONTEXT_PARAM_VM);
+		igt_assert_eq((int)set.param.value, expected);
+
+		gem_context_destroy(i915, create.ctx_id);
+
+		expected = set.param.value = vm_id[0];
+		set.param.ctx_id = ext.clone_id;
+		gem_context_set_param(i915, &set.param);
+
+		igt_assert_eq(__create_ext(i915, &create), 0);
+
+		set.param.ctx_id = create.ctx_id;
+		gem_context_get_param(i915, &set.param);
+
+		igt_assert_eq_u64(set.param.param, I915_CONTEXT_PARAM_VM);
+		igt_assert_eq((int)set.param.value, expected);
+
+		gem_context_destroy(i915, create.ctx_id);
+		ext.clone_id = gem_context_create(i915);
+	}
+
+	gem_context_destroy(i915, ext.clone_id);
+
+	for (int i = 0; i < ARRAY_SIZE(vm_id); i++)
+		gem_vm_destroy(i915, vm_id[i]);
+}
+
+igt_main
+{
+	int i915 = -1;
+
+	igt_fixture {
+		i915 = drm_open_driver(DRIVER_INTEL);
+		igt_require_gem(i915);
+		gem_require_contexts(i915);
+
+		igt_require(has_ctx_clone(i915));
+	}
+
+	igt_subtest("invalid")
+		invalid_clone(i915);
+
+	igt_subtest("engines")
+		clone_engines(i915);
+
+	igt_subtest("flags")
+		clone_flags(i915);
+
+	igt_subtest("scheduler")
+		clone_scheduler(i915);
+
+	igt_subtest("vm")
+		clone_vm(i915);
+
+	igt_fixture {
+		close(i915);
+	}
+}
diff --git a/tests/meson.build b/tests/meson.build
index 9015f809e..2b6132345 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -108,6 +108,7 @@ i915_progs = [
 	'gem_cs_prefetch',
 	'gem_cs_tlb',
 	'gem_ctx_bad_destroy',
+	'gem_ctx_clone',
 	'gem_ctx_create',
 	'gem_ctx_exec',
 	'gem_ctx_isolation',
-- 
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] 51+ messages in thread

* [igt-dev] [PATCH i-g-t 16/25] i915: Add gem_ctx_clone
@ 2019-03-14 14:19   ` Chris Wilson
  0 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

Exercise cloning contexts, an extension of merely creating one.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 tests/Makefile.sources     |   1 +
 tests/i915/gem_ctx_clone.c | 421 +++++++++++++++++++++++++++++++++++++
 tests/meson.build          |   1 +
 3 files changed, 423 insertions(+)
 create mode 100644 tests/i915/gem_ctx_clone.c

diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index 71ccf00af..6d0fe1c95 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_clone \
 	kms_3d \
 	kms_addfb_basic \
 	kms_atomic \
diff --git a/tests/i915/gem_ctx_clone.c b/tests/i915/gem_ctx_clone.c
new file mode 100644
index 000000000..7d83c6350
--- /dev/null
+++ b/tests/i915/gem_ctx_clone.c
@@ -0,0 +1,421 @@
+/*
+ * 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.
+ */
+
+#include "igt.h"
+#include "i915/gem_vm.h"
+#include "i915_drm.h"
+
+static int __create_ext(int i915, struct drm_i915_gem_context_create_ext *arg)
+{
+	int err;
+
+	err = 0;
+	if (igt_ioctl(i915, DRM_IOCTL_I915_GEM_CONTEXT_CREATE_EXT, arg))
+		err = -errno;
+
+	errno = 0;
+	return err;
+}
+
+static bool has_ctx_clone(int i915)
+{
+	struct drm_i915_gem_context_create_ext_clone ext = {
+		{ .name = I915_CONTEXT_CREATE_EXT_CLONE },
+		.clone_id = -1,
+	};
+	struct drm_i915_gem_context_create_ext create = {
+		.flags = I915_CONTEXT_CREATE_FLAGS_USE_EXTENSIONS,
+		.extensions = to_user_pointer(&ext),
+	};
+	return __create_ext(i915, &create) == -ENOENT;
+}
+
+static void invalid_clone(int i915)
+{
+	struct drm_i915_gem_context_create_ext_clone ext = {
+		{ .name = I915_CONTEXT_CREATE_EXT_CLONE },
+	};
+	struct drm_i915_gem_context_create_ext create = {
+		.flags = I915_CONTEXT_CREATE_FLAGS_USE_EXTENSIONS,
+		.extensions = to_user_pointer(&ext),
+	};
+
+	igt_assert_eq(__create_ext(i915, &create), 0);
+	gem_context_destroy(i915, create.ctx_id);
+
+	ext.flags = -1; /* Hopefully we won't run out of flags */
+	igt_assert_eq(__create_ext(i915, &create), -EINVAL);
+	ext.flags = 0;
+
+	ext.base.next_extension = -1;
+	igt_assert_eq(__create_ext(i915, &create), -EFAULT);
+	ext.base.next_extension = to_user_pointer(&ext);
+	igt_assert_eq(__create_ext(i915, &create), -E2BIG);
+	ext.base.next_extension = 0;
+
+	ext.clone_id = -1;
+	igt_assert_eq(__create_ext(i915, &create), -ENOENT);
+	ext.clone_id = 0;
+}
+
+static void clone_flags(int i915)
+{
+	struct drm_i915_gem_context_create_ext_setparam set = {
+		{ .name = I915_CONTEXT_CREATE_EXT_SETPARAM },
+		{ .param = I915_CONTEXT_PARAM_RECOVERABLE },
+	};
+	struct drm_i915_gem_context_create_ext_clone ext = {
+		{ .name = I915_CONTEXT_CREATE_EXT_CLONE },
+		.flags = I915_CONTEXT_CLONE_FLAGS,
+	};
+	struct drm_i915_gem_context_create_ext create = {
+		.flags = I915_CONTEXT_CREATE_FLAGS_USE_EXTENSIONS,
+		.extensions = to_user_pointer(&ext),
+	};
+	int expected;
+
+	set.param.value = 1; /* default is recoverable */
+	igt_require(__gem_context_set_param(i915, &set.param) == 0);
+
+	for (int pass = 0; pass < 2; pass++) { /* cloning default, then child */
+		igt_debug("Cloning %d\n", ext.clone_id);
+		igt_assert_eq(__create_ext(i915, &create), 0);
+
+		set.param.ctx_id = ext.clone_id;
+		gem_context_get_param(i915, &set.param);
+		expected = set.param.value;
+
+		set.param.ctx_id = create.ctx_id;
+		gem_context_get_param(i915, &set.param);
+
+		igt_assert_eq_u64(set.param.param,
+				  I915_CONTEXT_PARAM_RECOVERABLE);
+		igt_assert_eq((int)set.param.value, expected);
+
+		gem_context_destroy(i915, create.ctx_id);
+
+		expected = set.param.value = 0;
+		set.param.ctx_id = ext.clone_id;
+		gem_context_set_param(i915, &set.param);
+
+		igt_assert_eq(__create_ext(i915, &create), 0);
+
+		set.param.ctx_id = create.ctx_id;
+		gem_context_get_param(i915, &set.param);
+
+		igt_assert_eq_u64(set.param.param,
+				  I915_CONTEXT_PARAM_RECOVERABLE);
+		igt_assert_eq((int)set.param.value, expected);
+
+		gem_context_destroy(i915, create.ctx_id);
+
+		/* clone but then reset priority to default */
+		set.param.ctx_id = 0;
+		set.param.value = 1;
+		ext.base.next_extension = to_user_pointer(&set);
+		igt_assert_eq(__create_ext(i915, &create), 0);
+		ext.base.next_extension = 0;
+
+		set.param.ctx_id = create.ctx_id;
+		gem_context_get_param(i915, &set.param);
+		igt_assert_eq_u64(set.param.value, 1);
+
+		set.param.ctx_id = ext.clone_id;
+		gem_context_get_param(i915, &set.param);
+		igt_assert_eq_u64(set.param.value, 0);
+
+		gem_context_destroy(i915, create.ctx_id);
+		ext.clone_id = gem_context_create(i915);
+	}
+
+	gem_context_destroy(i915, ext.clone_id);
+}
+
+static void clone_engines(int i915)
+{
+	struct drm_i915_gem_context_create_ext_setparam set = {
+		{ .name = I915_CONTEXT_CREATE_EXT_SETPARAM },
+		{ .param = I915_CONTEXT_PARAM_ENGINES },
+	};
+	struct drm_i915_gem_context_create_ext_clone ext = {
+		{ .name = I915_CONTEXT_CREATE_EXT_CLONE },
+		.flags = I915_CONTEXT_CLONE_ENGINES,
+	};
+	struct drm_i915_gem_context_create_ext create = {
+		.flags = I915_CONTEXT_CREATE_FLAGS_USE_EXTENSIONS,
+		.extensions = to_user_pointer(&ext),
+	};
+	I915_DEFINE_CONTEXT_PARAM_ENGINES(expected, 64);
+	I915_DEFINE_CONTEXT_PARAM_ENGINES(engines, 64);
+	uint64_t ex_size;
+
+	memset(&expected, 0, sizeof(expected));
+	memset(&engines, 0, sizeof(engines));
+
+	igt_require(__gem_context_set_param(i915, &set.param) == 0);
+
+	for (int pass = 0; pass < 2; pass++) { /* cloning default, then child */
+		igt_debug("Cloning %d\n", ext.clone_id);
+		igt_assert_eq(__create_ext(i915, &create), 0);
+
+		set.param.ctx_id = ext.clone_id;
+		set.param.size = sizeof(expected);
+		set.param.value = to_user_pointer(&expected);
+		gem_context_get_param(i915, &set.param);
+		ex_size = set.param.size;
+
+		set.param.ctx_id = create.ctx_id;
+		set.param.size = sizeof(engines);
+		set.param.value = to_user_pointer(&engines);
+		gem_context_get_param(i915, &set.param);
+
+		igt_assert_eq_u64(set.param.param, I915_CONTEXT_PARAM_ENGINES);
+		igt_assert_eq_u64(set.param.size, ex_size);
+		igt_assert(!memcmp(&engines, &expected, ex_size));
+
+		gem_context_destroy(i915, create.ctx_id);
+
+		expected.class_instance[0].engine_class =
+			I915_ENGINE_CLASS_INVALID;
+		expected.class_instance[0].engine_instance =
+			I915_ENGINE_CLASS_INVALID_NONE;
+		ex_size = (sizeof(struct i915_context_param_engines) +
+			   sizeof(expected.class_instance[0]));
+
+		set.param.ctx_id = ext.clone_id;
+		set.param.size = ex_size;
+		set.param.value = to_user_pointer(&expected);
+		gem_context_set_param(i915, &set.param);
+
+		igt_assert_eq(__create_ext(i915, &create), 0);
+
+		set.param.ctx_id = create.ctx_id;
+		set.param.size = sizeof(engines);
+		set.param.value = to_user_pointer(&engines);
+		gem_context_get_param(i915, &set.param);
+
+		igt_assert_eq_u64(set.param.size, ex_size);
+		igt_assert(!memcmp(&engines, &expected, ex_size));
+
+		gem_context_destroy(i915, create.ctx_id);
+
+		/* clone but then reset engines to default */
+		set.param.ctx_id = 0;
+		set.param.size = 0;
+		set.param.value = 0;
+		ext.base.next_extension = to_user_pointer(&set);
+
+		igt_assert_eq(__create_ext(i915, &create), 0);
+		ext.base.next_extension = 0;
+
+		set.param.ctx_id = create.ctx_id;
+		set.param.size = sizeof(engines);
+		set.param.value = to_user_pointer(&engines);
+		gem_context_get_param(i915, &set.param);
+		igt_assert_eq_u64(set.param.size, 0);
+
+		gem_context_destroy(i915, create.ctx_id);
+
+		/* And check we ignore the flag */
+		ext.flags = 0;
+		igt_assert_eq(__create_ext(i915, &create), 0);
+		ext.flags = I915_CONTEXT_CLONE_ENGINES;
+
+		set.param.ctx_id = create.ctx_id;
+		set.param.size = sizeof(engines);
+		set.param.value = to_user_pointer(&engines);
+		gem_context_get_param(i915, &set.param);
+		igt_assert_eq_u64(set.param.size, 0);
+
+		ext.clone_id = gem_context_create(i915);
+	}
+
+	gem_context_destroy(i915, ext.clone_id);
+}
+
+static void clone_scheduler(int i915)
+{
+	struct drm_i915_gem_context_create_ext_setparam set = {
+		{ .name = I915_CONTEXT_CREATE_EXT_SETPARAM },
+		{ .param = I915_CONTEXT_PARAM_PRIORITY },
+	};
+	struct drm_i915_gem_context_create_ext_clone ext = {
+		{ .name = I915_CONTEXT_CREATE_EXT_CLONE },
+		.flags = I915_CONTEXT_CLONE_SCHEDATTR,
+	};
+	struct drm_i915_gem_context_create_ext create = {
+		.flags = I915_CONTEXT_CREATE_FLAGS_USE_EXTENSIONS,
+		.extensions = to_user_pointer(&ext),
+	};
+	int expected;
+
+	igt_require(__gem_context_set_param(i915, &set.param) == 0);
+
+	for (int pass = 0; pass < 2; pass++) { /* cloning default, then child */
+		igt_debug("Cloning %d\n", ext.clone_id);
+		igt_assert_eq(__create_ext(i915, &create), 0);
+
+		set.param.ctx_id = ext.clone_id;
+		gem_context_get_param(i915, &set.param);
+		expected = set.param.value;
+
+		set.param.ctx_id = create.ctx_id;
+		gem_context_get_param(i915, &set.param);
+
+		igt_assert_eq_u64(set.param.param, I915_CONTEXT_PARAM_PRIORITY);
+		igt_assert_eq((int)set.param.value, expected);
+
+		gem_context_destroy(i915, create.ctx_id);
+
+		expected = set.param.value = 1;
+		set.param.ctx_id = ext.clone_id;
+		gem_context_set_param(i915, &set.param);
+
+		igt_assert_eq(__create_ext(i915, &create), 0);
+
+		set.param.ctx_id = create.ctx_id;
+		gem_context_get_param(i915, &set.param);
+
+		igt_assert_eq_u64(set.param.param, I915_CONTEXT_PARAM_PRIORITY);
+		igt_assert_eq((int)set.param.value, expected);
+
+		gem_context_destroy(i915, create.ctx_id);
+
+		/* clone but then reset priority to default */
+		set.param.ctx_id = 0;
+		set.param.value = 0;
+		ext.base.next_extension = to_user_pointer(&set);
+		igt_assert_eq(__create_ext(i915, &create), 0);
+		ext.base.next_extension = 0;
+
+		set.param.ctx_id = create.ctx_id;
+		gem_context_get_param(i915, &set.param);
+		igt_assert_eq_u64(set.param.value, 0);
+
+		set.param.ctx_id = ext.clone_id;
+		gem_context_get_param(i915, &set.param);
+		igt_assert_eq_u64(set.param.value, 1);
+
+		gem_context_destroy(i915, create.ctx_id);
+		ext.clone_id = gem_context_create(i915);
+	}
+
+	gem_context_destroy(i915, ext.clone_id);
+}
+
+static void clone_vm(int i915)
+{
+	struct drm_i915_gem_context_create_ext_setparam set = {
+		{ .name = I915_CONTEXT_CREATE_EXT_SETPARAM },
+		{ .param = I915_CONTEXT_PARAM_VM },
+	};
+	struct drm_i915_gem_context_create_ext_clone ext = {
+		{ .name = I915_CONTEXT_CREATE_EXT_CLONE },
+		.flags = I915_CONTEXT_CLONE_VM,
+	};
+	struct drm_i915_gem_context_create_ext create = {
+		.flags = I915_CONTEXT_CREATE_FLAGS_USE_EXTENSIONS,
+		.extensions = to_user_pointer(&ext),
+	};
+	uint32_t vm_id[2];
+	int expected;
+
+	igt_require(__gem_context_set_param(i915, &set.param) == -ENOENT);
+
+	set.param.ctx_id = gem_context_create(i915);
+	gem_context_get_param(i915, &set.param);
+	vm_id[0] = set.param.value;
+	gem_context_destroy(i915, set.param.ctx_id);
+	set.param.ctx_id = 0;
+
+	vm_id[1] = gem_vm_create(i915);
+
+	for (int pass = 0; pass < 2; pass++) { /* cloning default, then child */
+		igt_debug("Cloning %d\n", ext.clone_id);
+		igt_assert_eq(__create_ext(i915, &create), 0);
+
+		set.param.ctx_id = ext.clone_id;
+		gem_context_get_param(i915, &set.param);
+		expected = set.param.value;
+
+		set.param.ctx_id = create.ctx_id;
+		gem_context_get_param(i915, &set.param);
+
+		igt_assert_eq_u64(set.param.param, I915_CONTEXT_PARAM_VM);
+		igt_assert_eq((int)set.param.value, expected);
+
+		gem_context_destroy(i915, create.ctx_id);
+
+		expected = set.param.value = vm_id[0];
+		set.param.ctx_id = ext.clone_id;
+		gem_context_set_param(i915, &set.param);
+
+		igt_assert_eq(__create_ext(i915, &create), 0);
+
+		set.param.ctx_id = create.ctx_id;
+		gem_context_get_param(i915, &set.param);
+
+		igt_assert_eq_u64(set.param.param, I915_CONTEXT_PARAM_VM);
+		igt_assert_eq((int)set.param.value, expected);
+
+		gem_context_destroy(i915, create.ctx_id);
+		ext.clone_id = gem_context_create(i915);
+	}
+
+	gem_context_destroy(i915, ext.clone_id);
+
+	for (int i = 0; i < ARRAY_SIZE(vm_id); i++)
+		gem_vm_destroy(i915, vm_id[i]);
+}
+
+igt_main
+{
+	int i915 = -1;
+
+	igt_fixture {
+		i915 = drm_open_driver(DRIVER_INTEL);
+		igt_require_gem(i915);
+		gem_require_contexts(i915);
+
+		igt_require(has_ctx_clone(i915));
+	}
+
+	igt_subtest("invalid")
+		invalid_clone(i915);
+
+	igt_subtest("engines")
+		clone_engines(i915);
+
+	igt_subtest("flags")
+		clone_flags(i915);
+
+	igt_subtest("scheduler")
+		clone_scheduler(i915);
+
+	igt_subtest("vm")
+		clone_vm(i915);
+
+	igt_fixture {
+		close(i915);
+	}
+}
diff --git a/tests/meson.build b/tests/meson.build
index 9015f809e..2b6132345 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -108,6 +108,7 @@ i915_progs = [
 	'gem_cs_prefetch',
 	'gem_cs_tlb',
 	'gem_ctx_bad_destroy',
+	'gem_ctx_clone',
 	'gem_ctx_create',
 	'gem_ctx_exec',
 	'gem_ctx_isolation',
-- 
2.20.1

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

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

* [PATCH i-g-t 17/25] i915: Add gem_vm_create
  2019-03-14 14:19 ` [igt-dev] " Chris Wilson
@ 2019-03-14 14:19   ` Chris Wilson
  -1 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

Exercise basic creation and swapping between new address spaces.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 lib/Makefile.sources       |   2 +
 lib/i915/gem_vm.c          | 130 ++++++++++++++++++++
 lib/i915/gem_vm.h          |  38 ++++++
 lib/meson.build            |   1 +
 tests/Makefile.sources     |   1 +
 tests/i915/gem_vm_create.c | 236 +++++++++++++++++++++++++++++++++++++
 tests/meson.build          |   1 +
 7 files changed, 409 insertions(+)
 create mode 100644 lib/i915/gem_vm.c
 create mode 100644 lib/i915/gem_vm.h
 create mode 100644 tests/i915/gem_vm_create.c

diff --git a/lib/Makefile.sources b/lib/Makefile.sources
index e00347f94..a7074209a 100644
--- a/lib/Makefile.sources
+++ b/lib/Makefile.sources
@@ -13,6 +13,8 @@ lib_source_list =	 	\
 	i915/gem_ring.c	\
 	i915/gem_mman.c	\
 	i915/gem_mman.h	\
+	i915/gem_vm.c	\
+	i915/gem_vm.h	\
 	i915_3d.h		\
 	i915_reg.h		\
 	i915_pciids.h		\
diff --git a/lib/i915/gem_vm.c b/lib/i915/gem_vm.c
new file mode 100644
index 000000000..e0d46d509
--- /dev/null
+++ b/lib/i915/gem_vm.c
@@ -0,0 +1,130 @@
+/*
+ * 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.
+ */
+
+#include <errno.h>
+#include <string.h>
+
+#include "ioctl_wrappers.h"
+#include "drmtest.h"
+
+#include "i915/gem_vm.h"
+
+/**
+ * SECTION:gem_vm
+ * @short_description: Helpers for dealing with address spaces (vm/GTT)
+ * @title: GEM Virtual Memory
+ *
+ * This helper library contains functions used for handling gem address
+ * spaces..
+ */
+
+/**
+ * gem_has_vm:
+ * @i915: open i915 drm file descriptor
+ *
+ * Returns: whether VM creation is supported or not.
+ */
+bool gem_has_vm(int i915)
+{
+	uint32_t vm_id = 0;
+
+	__gem_vm_create(i915, &vm_id);
+	if (vm_id)
+		gem_vm_destroy(i915, vm_id);
+
+	return vm_id;
+}
+
+/**
+ * gem_require_vm:
+ * @i915: open i915 drm file descriptor
+ *
+ * This helper will automatically skip the test on platforms where address
+ * space creation is not available.
+ */
+void gem_require_vm(int i915)
+{
+	igt_require(gem_has_vm(i915));
+}
+
+int __gem_vm_create(int i915, uint32_t *vm_id)
+{
+       struct drm_i915_gem_vm_control ctl = {};
+       int err = 0;
+
+       if (igt_ioctl(i915, DRM_IOCTL_I915_GEM_VM_CREATE, &ctl) == 0) {
+               *vm_id = ctl.vm_id;
+       } else {
+	       err = -errno;
+	       igt_assume(err != 0);
+       }
+
+       errno = 0;
+       return err;
+}
+
+/**
+ * gem_vm_create:
+ * @i915: open i915 drm file descriptor
+ *
+ * This wraps the VM_CREATE ioctl, which is used to allocate a new
+ * vm_set_caching() this wrapper skips on
+ * kernels and platforms where address space support is not available.
+ *
+ * Returns: The id of the allocated address space.
+ */
+uint32_t gem_vm_create(int i915)
+{
+	uint32_t vm_id;
+
+	igt_assert_eq(__gem_vm_create(i915, &vm_id), 0);
+	igt_assert(vm_id != 0);
+
+	return vm_id;
+}
+
+int __gem_vm_destroy(int i915, uint32_t vm_id)
+{
+	struct drm_i915_gem_vm_control ctl = { .vm_id = vm_id };
+	int err = 0;
+
+	if (igt_ioctl(i915, DRM_IOCTL_I915_GEM_VM_DESTROY, &ctl)) {
+		err = -errno;
+		igt_assume(err);
+	}
+
+	errno = 0;
+	return err;
+}
+
+/**
+ * gem_vm_destroy:
+ * @i915: open i915 drm file descriptor
+ * @vm_id: i915 VM id
+ *
+ * This wraps the VM_DESTROY ioctl, which is used to free an address space.
+ */
+void gem_vm_destroy(int i915, uint32_t vm_id)
+{
+	igt_assert_eq(__gem_vm_destroy(i915, vm_id), 0);
+}
diff --git a/lib/i915/gem_vm.h b/lib/i915/gem_vm.h
new file mode 100644
index 000000000..27af899d4
--- /dev/null
+++ b/lib/i915/gem_vm.h
@@ -0,0 +1,38 @@
+/*
+ * 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 GEM_VM_H
+#define GEM_VM_H
+
+#include <stdint.h>
+
+bool gem_has_vm(int i915);
+void gem_require_vm(int i915);
+
+uint32_t gem_vm_create(int i915);
+int __gem_vm_create(int i915, uint32_t *vm_id);
+
+void gem_vm_destroy(int i915, uint32_t vm_id);
+int __gem_vm_destroy(int i915, uint32_t vm_id);
+
+#endif /* GEM_VM_H */
diff --git a/lib/meson.build b/lib/meson.build
index 89de06e69..f95922330 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -5,6 +5,7 @@ lib_sources = [
 	'i915/gem_submission.c',
 	'i915/gem_ring.c',
 	'i915/gem_mman.c',
+	'i915/gem_vm.c',
 	'igt_color_encoding.c',
 	'igt_debugfs.c',
 	'igt_device.c',
diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index 6d0fe1c95..12f4bbd75 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -22,6 +22,7 @@ TESTS_progs = \
 	drm_mm \
 	drm_read \
 	i915/gem_ctx_clone \
+	i915/gem_vm_create \
 	kms_3d \
 	kms_addfb_basic \
 	kms_atomic \
diff --git a/tests/i915/gem_vm_create.c b/tests/i915/gem_vm_create.c
new file mode 100644
index 000000000..0264fa301
--- /dev/null
+++ b/tests/i915/gem_vm_create.c
@@ -0,0 +1,236 @@
+/*
+ * 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.
+ */
+
+#include "igt.h"
+#include "igt_dummyload.h"
+#include "i915/gem_vm.h"
+
+static int __gem_vm_create_local(int i915, struct drm_i915_gem_vm_control *ctl)
+{
+	int err = 0;
+	if (igt_ioctl(i915, DRM_IOCTL_I915_GEM_VM_CREATE, ctl)) {
+		err = -errno;
+		igt_assume(err);
+	}
+	errno = 0;
+	return err;
+}
+
+static int __gem_vm_destroy_local(int i915, struct drm_i915_gem_vm_control *ctl)
+{
+	int err = 0;
+	if (igt_ioctl(i915, DRM_IOCTL_I915_GEM_VM_DESTROY, ctl)) {
+		err = -errno;
+		igt_assume(err);
+	}
+	errno = 0;
+	return err;
+}
+
+static bool has_vm(int i915)
+{
+	struct drm_i915_gem_vm_control ctl = {};
+	int err;
+
+	err = __gem_vm_create_local(i915, &ctl);
+	switch (err) {
+	case -EINVAL: /* unknown ioctl */
+	case -ENODEV: /* !full-ppgtt */
+		return false;
+
+	case 0:
+		gem_vm_destroy(i915, ctl.vm_id);
+		return true;
+
+	default:
+		igt_fail_on_f(err, "Unknown response from VM_CREATE\n");
+		return false;
+	}
+}
+
+static void invalid_create(int i915)
+{
+	struct drm_i915_gem_vm_control ctl = {};
+	struct i915_user_extension ext = { .name = -1 };
+
+	igt_assert_eq(__gem_vm_create_local(i915, &ctl), 0);
+	gem_vm_destroy(i915, ctl.vm_id);
+
+	ctl.vm_id = 0xdeadbeef;
+	igt_assert_eq(__gem_vm_create_local(i915, &ctl), 0);
+	gem_vm_destroy(i915, ctl.vm_id);
+	ctl.vm_id = 0;
+
+	ctl.flags = -1;
+	igt_assert_eq(__gem_vm_create_local(i915, &ctl), -EINVAL);
+	ctl.flags = 0;
+
+	ctl.extensions = -1;
+	igt_assert_eq(__gem_vm_create_local(i915, &ctl), -EFAULT);
+	ctl.extensions = to_user_pointer(&ext);
+	igt_assert_eq(__gem_vm_create_local(i915, &ctl), -EINVAL);
+	ctl.extensions = 0;
+}
+
+static void invalid_destroy(int i915)
+{
+	struct drm_i915_gem_vm_control ctl = {};
+
+	igt_assert_eq(__gem_vm_destroy_local(i915, &ctl), -ENOENT);
+
+	igt_assert_eq(__gem_vm_create_local(i915, &ctl), 0);
+	igt_assert_eq(__gem_vm_destroy_local(i915, &ctl), 0);
+	igt_assert_eq(__gem_vm_destroy_local(i915, &ctl), -ENOENT);
+
+	igt_assert_eq(__gem_vm_create_local(i915, &ctl), 0);
+	ctl.vm_id = ctl.vm_id + 1; /* XXX assume cyclic allocator */
+	igt_assert_eq(__gem_vm_destroy_local(i915, &ctl), -ENOENT);
+	ctl.vm_id = ctl.vm_id - 1;
+	igt_assert_eq(__gem_vm_destroy_local(i915, &ctl), 0);
+
+	igt_assert_eq(__gem_vm_create_local(i915, &ctl), 0);
+	ctl.flags = -1;
+	igt_assert_eq(__gem_vm_destroy_local(i915, &ctl), -EINVAL);
+	ctl.flags = 0;
+	igt_assert_eq(__gem_vm_destroy_local(i915, &ctl), 0);
+
+	igt_assert_eq(__gem_vm_create_local(i915, &ctl), 0);
+	ctl.extensions = -1;
+	igt_assert_eq(__gem_vm_destroy_local(i915, &ctl), -EINVAL);
+	ctl.extensions = 0;
+	igt_assert_eq(__gem_vm_destroy_local(i915, &ctl), 0);
+}
+
+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 execbuf(int i915)
+{
+	struct drm_i915_gem_exec_object2 batch = {
+		.handle = batch_create(i915),
+	};
+	struct drm_i915_gem_execbuffer2 eb = {
+		.buffers_ptr = to_user_pointer(&batch),
+		.buffer_count = 1,
+	};
+	struct drm_i915_gem_context_param arg = {
+		.param = I915_CONTEXT_PARAM_VM,
+	};
+
+	/* First verify that we try to use "softpinning" by default */
+	batch.offset = 48 << 20;
+	gem_execbuf(i915, &eb);
+	igt_assert_eq_u64(batch.offset, 48 << 20);
+
+	arg.value = gem_vm_create(i915);
+	gem_context_set_param(i915, &arg);
+	gem_execbuf(i915, &eb);
+	igt_assert_eq_u64(batch.offset, 48 << 20);
+	gem_vm_destroy(i915, arg.value);
+
+	arg.value = gem_vm_create(i915);
+	gem_context_set_param(i915, &arg);
+	batch.offset = 0;
+	gem_execbuf(i915, &eb);
+	igt_assert_eq_u64(batch.offset, 0);
+	gem_vm_destroy(i915, arg.value);
+
+	gem_sync(i915, batch.handle);
+	gem_close(i915, batch.handle);
+}
+
+static void async_destroy(int i915)
+{
+	struct drm_i915_gem_context_param arg = {
+		.ctx_id = gem_context_create(i915),
+		.value = gem_vm_create(i915),
+		.param = I915_CONTEXT_PARAM_VM,
+	};
+	igt_spin_t *spin[2];
+
+	spin[0] = igt_spin_batch_new(i915,
+				     .ctx = arg.ctx_id,
+				     .flags = IGT_SPIN_POLL_RUN);
+	igt_spin_busywait_until_running(spin[0]);
+
+	gem_context_set_param(i915, &arg);
+	spin[1] = __igt_spin_batch_new(i915, .ctx = arg.ctx_id);
+
+	igt_spin_batch_end(spin[0]);
+	gem_sync(i915, spin[0]->handle);
+
+	gem_vm_destroy(i915, arg.value);
+	gem_context_destroy(i915, arg.ctx_id);
+
+	igt_spin_batch_end(spin[1]);
+	gem_sync(i915, spin[1]->handle);
+
+	for (int i = 0; i < ARRAY_SIZE(spin); i++)
+		igt_spin_batch_free(i915, spin[i]);
+}
+
+igt_main
+{
+	int i915 = -1;
+
+	igt_fixture {
+		i915 = drm_open_driver(DRIVER_INTEL);
+		igt_require_gem(i915);
+		igt_require(has_vm(i915));
+	}
+
+	igt_subtest("invalid-create")
+		invalid_create(i915);
+
+	igt_subtest("invalid-destroy")
+		invalid_destroy(i915);
+
+	igt_subtest_group {
+		igt_fixture {
+			gem_context_require_param(i915, I915_CONTEXT_PARAM_VM);
+		}
+
+		igt_subtest("execbuf")
+			execbuf(i915);
+
+		igt_subtest("async-destroy")
+			async_destroy(i915);
+	}
+
+	igt_fixture {
+		close(i915);
+	}
+}
diff --git a/tests/meson.build b/tests/meson.build
index 2b6132345..7c194c3e3 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -210,6 +210,7 @@ i915_progs = [
 	'gem_unfence_active_buffers',
 	'gem_unref_active_buffers',
 	'gem_userptr_blits',
+	'gem_vm_create',
 	'gem_wait',
 	'gem_workarounds',
 	'gem_write_read_ring_switch',
-- 
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] 51+ messages in thread

* [Intel-gfx] [PATCH i-g-t 17/25] i915: Add gem_vm_create
@ 2019-03-14 14:19   ` Chris Wilson
  0 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

Exercise basic creation and swapping between new address spaces.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 lib/Makefile.sources       |   2 +
 lib/i915/gem_vm.c          | 130 ++++++++++++++++++++
 lib/i915/gem_vm.h          |  38 ++++++
 lib/meson.build            |   1 +
 tests/Makefile.sources     |   1 +
 tests/i915/gem_vm_create.c | 236 +++++++++++++++++++++++++++++++++++++
 tests/meson.build          |   1 +
 7 files changed, 409 insertions(+)
 create mode 100644 lib/i915/gem_vm.c
 create mode 100644 lib/i915/gem_vm.h
 create mode 100644 tests/i915/gem_vm_create.c

diff --git a/lib/Makefile.sources b/lib/Makefile.sources
index e00347f94..a7074209a 100644
--- a/lib/Makefile.sources
+++ b/lib/Makefile.sources
@@ -13,6 +13,8 @@ lib_source_list =	 	\
 	i915/gem_ring.c	\
 	i915/gem_mman.c	\
 	i915/gem_mman.h	\
+	i915/gem_vm.c	\
+	i915/gem_vm.h	\
 	i915_3d.h		\
 	i915_reg.h		\
 	i915_pciids.h		\
diff --git a/lib/i915/gem_vm.c b/lib/i915/gem_vm.c
new file mode 100644
index 000000000..e0d46d509
--- /dev/null
+++ b/lib/i915/gem_vm.c
@@ -0,0 +1,130 @@
+/*
+ * 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.
+ */
+
+#include <errno.h>
+#include <string.h>
+
+#include "ioctl_wrappers.h"
+#include "drmtest.h"
+
+#include "i915/gem_vm.h"
+
+/**
+ * SECTION:gem_vm
+ * @short_description: Helpers for dealing with address spaces (vm/GTT)
+ * @title: GEM Virtual Memory
+ *
+ * This helper library contains functions used for handling gem address
+ * spaces..
+ */
+
+/**
+ * gem_has_vm:
+ * @i915: open i915 drm file descriptor
+ *
+ * Returns: whether VM creation is supported or not.
+ */
+bool gem_has_vm(int i915)
+{
+	uint32_t vm_id = 0;
+
+	__gem_vm_create(i915, &vm_id);
+	if (vm_id)
+		gem_vm_destroy(i915, vm_id);
+
+	return vm_id;
+}
+
+/**
+ * gem_require_vm:
+ * @i915: open i915 drm file descriptor
+ *
+ * This helper will automatically skip the test on platforms where address
+ * space creation is not available.
+ */
+void gem_require_vm(int i915)
+{
+	igt_require(gem_has_vm(i915));
+}
+
+int __gem_vm_create(int i915, uint32_t *vm_id)
+{
+       struct drm_i915_gem_vm_control ctl = {};
+       int err = 0;
+
+       if (igt_ioctl(i915, DRM_IOCTL_I915_GEM_VM_CREATE, &ctl) == 0) {
+               *vm_id = ctl.vm_id;
+       } else {
+	       err = -errno;
+	       igt_assume(err != 0);
+       }
+
+       errno = 0;
+       return err;
+}
+
+/**
+ * gem_vm_create:
+ * @i915: open i915 drm file descriptor
+ *
+ * This wraps the VM_CREATE ioctl, which is used to allocate a new
+ * vm_set_caching() this wrapper skips on
+ * kernels and platforms where address space support is not available.
+ *
+ * Returns: The id of the allocated address space.
+ */
+uint32_t gem_vm_create(int i915)
+{
+	uint32_t vm_id;
+
+	igt_assert_eq(__gem_vm_create(i915, &vm_id), 0);
+	igt_assert(vm_id != 0);
+
+	return vm_id;
+}
+
+int __gem_vm_destroy(int i915, uint32_t vm_id)
+{
+	struct drm_i915_gem_vm_control ctl = { .vm_id = vm_id };
+	int err = 0;
+
+	if (igt_ioctl(i915, DRM_IOCTL_I915_GEM_VM_DESTROY, &ctl)) {
+		err = -errno;
+		igt_assume(err);
+	}
+
+	errno = 0;
+	return err;
+}
+
+/**
+ * gem_vm_destroy:
+ * @i915: open i915 drm file descriptor
+ * @vm_id: i915 VM id
+ *
+ * This wraps the VM_DESTROY ioctl, which is used to free an address space.
+ */
+void gem_vm_destroy(int i915, uint32_t vm_id)
+{
+	igt_assert_eq(__gem_vm_destroy(i915, vm_id), 0);
+}
diff --git a/lib/i915/gem_vm.h b/lib/i915/gem_vm.h
new file mode 100644
index 000000000..27af899d4
--- /dev/null
+++ b/lib/i915/gem_vm.h
@@ -0,0 +1,38 @@
+/*
+ * 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 GEM_VM_H
+#define GEM_VM_H
+
+#include <stdint.h>
+
+bool gem_has_vm(int i915);
+void gem_require_vm(int i915);
+
+uint32_t gem_vm_create(int i915);
+int __gem_vm_create(int i915, uint32_t *vm_id);
+
+void gem_vm_destroy(int i915, uint32_t vm_id);
+int __gem_vm_destroy(int i915, uint32_t vm_id);
+
+#endif /* GEM_VM_H */
diff --git a/lib/meson.build b/lib/meson.build
index 89de06e69..f95922330 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -5,6 +5,7 @@ lib_sources = [
 	'i915/gem_submission.c',
 	'i915/gem_ring.c',
 	'i915/gem_mman.c',
+	'i915/gem_vm.c',
 	'igt_color_encoding.c',
 	'igt_debugfs.c',
 	'igt_device.c',
diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index 6d0fe1c95..12f4bbd75 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -22,6 +22,7 @@ TESTS_progs = \
 	drm_mm \
 	drm_read \
 	i915/gem_ctx_clone \
+	i915/gem_vm_create \
 	kms_3d \
 	kms_addfb_basic \
 	kms_atomic \
diff --git a/tests/i915/gem_vm_create.c b/tests/i915/gem_vm_create.c
new file mode 100644
index 000000000..0264fa301
--- /dev/null
+++ b/tests/i915/gem_vm_create.c
@@ -0,0 +1,236 @@
+/*
+ * 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.
+ */
+
+#include "igt.h"
+#include "igt_dummyload.h"
+#include "i915/gem_vm.h"
+
+static int __gem_vm_create_local(int i915, struct drm_i915_gem_vm_control *ctl)
+{
+	int err = 0;
+	if (igt_ioctl(i915, DRM_IOCTL_I915_GEM_VM_CREATE, ctl)) {
+		err = -errno;
+		igt_assume(err);
+	}
+	errno = 0;
+	return err;
+}
+
+static int __gem_vm_destroy_local(int i915, struct drm_i915_gem_vm_control *ctl)
+{
+	int err = 0;
+	if (igt_ioctl(i915, DRM_IOCTL_I915_GEM_VM_DESTROY, ctl)) {
+		err = -errno;
+		igt_assume(err);
+	}
+	errno = 0;
+	return err;
+}
+
+static bool has_vm(int i915)
+{
+	struct drm_i915_gem_vm_control ctl = {};
+	int err;
+
+	err = __gem_vm_create_local(i915, &ctl);
+	switch (err) {
+	case -EINVAL: /* unknown ioctl */
+	case -ENODEV: /* !full-ppgtt */
+		return false;
+
+	case 0:
+		gem_vm_destroy(i915, ctl.vm_id);
+		return true;
+
+	default:
+		igt_fail_on_f(err, "Unknown response from VM_CREATE\n");
+		return false;
+	}
+}
+
+static void invalid_create(int i915)
+{
+	struct drm_i915_gem_vm_control ctl = {};
+	struct i915_user_extension ext = { .name = -1 };
+
+	igt_assert_eq(__gem_vm_create_local(i915, &ctl), 0);
+	gem_vm_destroy(i915, ctl.vm_id);
+
+	ctl.vm_id = 0xdeadbeef;
+	igt_assert_eq(__gem_vm_create_local(i915, &ctl), 0);
+	gem_vm_destroy(i915, ctl.vm_id);
+	ctl.vm_id = 0;
+
+	ctl.flags = -1;
+	igt_assert_eq(__gem_vm_create_local(i915, &ctl), -EINVAL);
+	ctl.flags = 0;
+
+	ctl.extensions = -1;
+	igt_assert_eq(__gem_vm_create_local(i915, &ctl), -EFAULT);
+	ctl.extensions = to_user_pointer(&ext);
+	igt_assert_eq(__gem_vm_create_local(i915, &ctl), -EINVAL);
+	ctl.extensions = 0;
+}
+
+static void invalid_destroy(int i915)
+{
+	struct drm_i915_gem_vm_control ctl = {};
+
+	igt_assert_eq(__gem_vm_destroy_local(i915, &ctl), -ENOENT);
+
+	igt_assert_eq(__gem_vm_create_local(i915, &ctl), 0);
+	igt_assert_eq(__gem_vm_destroy_local(i915, &ctl), 0);
+	igt_assert_eq(__gem_vm_destroy_local(i915, &ctl), -ENOENT);
+
+	igt_assert_eq(__gem_vm_create_local(i915, &ctl), 0);
+	ctl.vm_id = ctl.vm_id + 1; /* XXX assume cyclic allocator */
+	igt_assert_eq(__gem_vm_destroy_local(i915, &ctl), -ENOENT);
+	ctl.vm_id = ctl.vm_id - 1;
+	igt_assert_eq(__gem_vm_destroy_local(i915, &ctl), 0);
+
+	igt_assert_eq(__gem_vm_create_local(i915, &ctl), 0);
+	ctl.flags = -1;
+	igt_assert_eq(__gem_vm_destroy_local(i915, &ctl), -EINVAL);
+	ctl.flags = 0;
+	igt_assert_eq(__gem_vm_destroy_local(i915, &ctl), 0);
+
+	igt_assert_eq(__gem_vm_create_local(i915, &ctl), 0);
+	ctl.extensions = -1;
+	igt_assert_eq(__gem_vm_destroy_local(i915, &ctl), -EINVAL);
+	ctl.extensions = 0;
+	igt_assert_eq(__gem_vm_destroy_local(i915, &ctl), 0);
+}
+
+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 execbuf(int i915)
+{
+	struct drm_i915_gem_exec_object2 batch = {
+		.handle = batch_create(i915),
+	};
+	struct drm_i915_gem_execbuffer2 eb = {
+		.buffers_ptr = to_user_pointer(&batch),
+		.buffer_count = 1,
+	};
+	struct drm_i915_gem_context_param arg = {
+		.param = I915_CONTEXT_PARAM_VM,
+	};
+
+	/* First verify that we try to use "softpinning" by default */
+	batch.offset = 48 << 20;
+	gem_execbuf(i915, &eb);
+	igt_assert_eq_u64(batch.offset, 48 << 20);
+
+	arg.value = gem_vm_create(i915);
+	gem_context_set_param(i915, &arg);
+	gem_execbuf(i915, &eb);
+	igt_assert_eq_u64(batch.offset, 48 << 20);
+	gem_vm_destroy(i915, arg.value);
+
+	arg.value = gem_vm_create(i915);
+	gem_context_set_param(i915, &arg);
+	batch.offset = 0;
+	gem_execbuf(i915, &eb);
+	igt_assert_eq_u64(batch.offset, 0);
+	gem_vm_destroy(i915, arg.value);
+
+	gem_sync(i915, batch.handle);
+	gem_close(i915, batch.handle);
+}
+
+static void async_destroy(int i915)
+{
+	struct drm_i915_gem_context_param arg = {
+		.ctx_id = gem_context_create(i915),
+		.value = gem_vm_create(i915),
+		.param = I915_CONTEXT_PARAM_VM,
+	};
+	igt_spin_t *spin[2];
+
+	spin[0] = igt_spin_batch_new(i915,
+				     .ctx = arg.ctx_id,
+				     .flags = IGT_SPIN_POLL_RUN);
+	igt_spin_busywait_until_running(spin[0]);
+
+	gem_context_set_param(i915, &arg);
+	spin[1] = __igt_spin_batch_new(i915, .ctx = arg.ctx_id);
+
+	igt_spin_batch_end(spin[0]);
+	gem_sync(i915, spin[0]->handle);
+
+	gem_vm_destroy(i915, arg.value);
+	gem_context_destroy(i915, arg.ctx_id);
+
+	igt_spin_batch_end(spin[1]);
+	gem_sync(i915, spin[1]->handle);
+
+	for (int i = 0; i < ARRAY_SIZE(spin); i++)
+		igt_spin_batch_free(i915, spin[i]);
+}
+
+igt_main
+{
+	int i915 = -1;
+
+	igt_fixture {
+		i915 = drm_open_driver(DRIVER_INTEL);
+		igt_require_gem(i915);
+		igt_require(has_vm(i915));
+	}
+
+	igt_subtest("invalid-create")
+		invalid_create(i915);
+
+	igt_subtest("invalid-destroy")
+		invalid_destroy(i915);
+
+	igt_subtest_group {
+		igt_fixture {
+			gem_context_require_param(i915, I915_CONTEXT_PARAM_VM);
+		}
+
+		igt_subtest("execbuf")
+			execbuf(i915);
+
+		igt_subtest("async-destroy")
+			async_destroy(i915);
+	}
+
+	igt_fixture {
+		close(i915);
+	}
+}
diff --git a/tests/meson.build b/tests/meson.build
index 2b6132345..7c194c3e3 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -210,6 +210,7 @@ i915_progs = [
 	'gem_unfence_active_buffers',
 	'gem_unref_active_buffers',
 	'gem_userptr_blits',
+	'gem_vm_create',
 	'gem_wait',
 	'gem_workarounds',
 	'gem_write_read_ring_switch',
-- 
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] 51+ messages in thread

* [PATCH i-g-t 18/25] i915: Exercise creating context with shared GTT
  2019-03-14 14:19 ` [igt-dev] " Chris Wilson
@ 2019-03-14 14:19   ` Chris Wilson
  -1 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

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   | 890 ++++++++++++++++++++++++++++++++++
 tests/i915/gem_exec_whisper.c |  32 +-
 tests/meson.build             |   1 +
 6 files changed, 996 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 8b4d5b704..e005192d4 100644
--- a/lib/i915/gem_context.c
+++ b/lib/i915/gem_context.c
@@ -271,3 +271,71 @@ void gem_context_set_priority(int fd, uint32_t ctx_id, int prio)
 {
 	igt_assert_eq(__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_id = 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 12f4bbd75..7e06c969f 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -22,6 +22,7 @@ TESTS_progs = \
 	drm_mm \
 	drm_read \
 	i915/gem_ctx_clone \
+	i915/gem_ctx_shared \
 	i915/gem_vm_create \
 	kms_3d \
 	kms_addfb_basic \
diff --git a/tests/i915/gem_ctx_shared.c b/tests/i915/gem_ctx_shared.c
new file mode 100644
index 000000000..426155356
--- /dev/null
+++ b/tests/i915/gem_ctx_shared.c
@@ -0,0 +1,890 @@
+/*
+ * 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,
+	};
+
+	igt_require(gem_has_execlists(i915));
+
+	/*
+	 * 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 = ctx, .engine = other);
+		} else {
+			struct drm_i915_gem_execbuffer2 execbuf = {
+				.buffers_ptr = spin->execbuf.buffers_ptr,
+				.buffer_count = spin->execbuf.buffer_count,
+				.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 = ctx, .engine = other);
+		} else {
+			struct drm_i915_gem_execbuffer2 execbuf = {
+				.buffers_ptr = spin->execbuf.buffers_ptr,
+				.buffer_count = spin->execbuf.buffer_count,
+				.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 7c194c3e3..e005c79c4 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -113,6 +113,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] 51+ messages in thread

* [igt-dev] [PATCH i-g-t 18/25] i915: Exercise creating context with shared GTT
@ 2019-03-14 14:19   ` Chris Wilson
  0 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: Tvrtko Ursulin, igt-dev

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   | 890 ++++++++++++++++++++++++++++++++++
 tests/i915/gem_exec_whisper.c |  32 +-
 tests/meson.build             |   1 +
 6 files changed, 996 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 8b4d5b704..e005192d4 100644
--- a/lib/i915/gem_context.c
+++ b/lib/i915/gem_context.c
@@ -271,3 +271,71 @@ void gem_context_set_priority(int fd, uint32_t ctx_id, int prio)
 {
 	igt_assert_eq(__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_id = 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 12f4bbd75..7e06c969f 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -22,6 +22,7 @@ TESTS_progs = \
 	drm_mm \
 	drm_read \
 	i915/gem_ctx_clone \
+	i915/gem_ctx_shared \
 	i915/gem_vm_create \
 	kms_3d \
 	kms_addfb_basic \
diff --git a/tests/i915/gem_ctx_shared.c b/tests/i915/gem_ctx_shared.c
new file mode 100644
index 000000000..426155356
--- /dev/null
+++ b/tests/i915/gem_ctx_shared.c
@@ -0,0 +1,890 @@
+/*
+ * 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,
+	};
+
+	igt_require(gem_has_execlists(i915));
+
+	/*
+	 * 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 = ctx, .engine = other);
+		} else {
+			struct drm_i915_gem_execbuffer2 execbuf = {
+				.buffers_ptr = spin->execbuf.buffers_ptr,
+				.buffer_count = spin->execbuf.buffer_count,
+				.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 = ctx, .engine = other);
+		} else {
+			struct drm_i915_gem_execbuffer2 execbuf = {
+				.buffers_ptr = spin->execbuf.buffers_ptr,
+				.buffer_count = spin->execbuf.buffer_count,
+				.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 7c194c3e3..e005c79c4 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -113,6 +113,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

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

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

* [PATCH i-g-t 19/25] i915/gem_ctx_switch: Exercise queues
  2019-03-14 14:19 ` [igt-dev] " Chris Wilson
@ 2019-03-14 14:19   ` Chris Wilson
  -1 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

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] 51+ messages in thread

* [igt-dev] [PATCH i-g-t 19/25] i915/gem_ctx_switch: Exercise queues
@ 2019-03-14 14:19   ` Chris Wilson
  0 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

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

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

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

* [PATCH i-g-t 20/25] i915/gem_exec_whisper: Fork all-engine tests one-per-engine
  2019-03-14 14:19 ` [igt-dev] " Chris Wilson
@ 2019-03-14 14:19   ` Chris Wilson
  -1 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

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] 51+ messages in thread

* [igt-dev] [PATCH i-g-t 20/25] i915/gem_exec_whisper: Fork all-engine tests one-per-engine
@ 2019-03-14 14:19   ` Chris Wilson
  0 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

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

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

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

* [PATCH i-g-t 21/25] i915/gem_exec_whisper: debugfs/next_seqno is defunct
  2019-03-14 14:19 ` [igt-dev] " Chris Wilson
@ 2019-03-14 14:19   ` Chris Wilson
  -1 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

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] 51+ messages in thread

* [Intel-gfx] [PATCH i-g-t 21/25] i915/gem_exec_whisper: debugfs/next_seqno is defunct
@ 2019-03-14 14:19   ` Chris Wilson
  0 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

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] 51+ messages in thread

* [PATCH i-g-t 22/25] i915: Add gem_ctx_engines
  2019-03-14 14:19 ` [igt-dev] " Chris Wilson
@ 2019-03-14 14:19   ` Chris Wilson
  -1 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

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 | 441 +++++++++++++++++++++++++++++++++++
 tests/meson.build            |   1 +
 3 files changed, 443 insertions(+)
 create mode 100644 tests/i915/gem_ctx_engines.c

diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index 7e06c969f..2f5612b07 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -22,6 +22,7 @@ TESTS_progs = \
 	drm_mm \
 	drm_read \
 	i915/gem_ctx_clone \
+	i915/gem_ctx_engines \
 	i915/gem_ctx_shared \
 	i915/gem_vm_create \
 	kms_3d \
diff --git a/tests/i915/gem_ctx_engines.c b/tests/i915/gem_ctx_engines.c
new file mode 100644
index 000000000..588ecf196
--- /dev/null
+++ b/tests/i915/gem_ctx_engines.c
@@ -0,0 +1,441 @@
+/*
+ * 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 idempotent(int i915)
+{
+	I915_DEFINE_CONTEXT_PARAM_ENGINES(engines , I915_EXEC_RING_MASK + 1);
+	I915_DEFINE_CONTEXT_PARAM_ENGINES(expected , I915_EXEC_RING_MASK + 1);
+	struct drm_i915_gem_context_param p = {
+		.ctx_id = gem_context_create(i915),
+		.param = I915_CONTEXT_PARAM_ENGINES,
+		.value = to_user_pointer(&engines),
+		.size = sizeof(engines),
+	};
+	const size_t base = sizeof(struct i915_context_param_engines);
+	const struct intel_execution_engine2 *e;
+	int idx;
+
+	/* What goes in, must come out. And what comes out, must go in */
+
+	gem_context_get_param(i915, &p);
+	igt_assert_eq(p.size, 0); /* atm default is to use legacy ring mask */
+
+	idx = 0;
+	memset(&engines, 0, sizeof(engines));
+	for_each_engine_class_instance(i915, e) {
+		engines.class_instance[idx].engine_class = e->class;
+		engines.class_instance[idx].engine_instance = e->instance;
+		idx++;
+	}
+	idx *= sizeof(*engines.class_instance);
+	p.size = base + idx;
+	gem_context_set_param(i915, &p);
+
+	memcpy(&expected, &engines, sizeof(expected));
+
+	gem_context_get_param(i915, &p);
+	igt_assert_eq(p.size, base + idx);
+	igt_assert(!memcmp(&expected, &engines, idx));
+
+	p.size = base;
+	gem_context_set_param(i915, &p);
+	gem_context_get_param(i915, &p);
+	igt_assert_eq(p.size, base);
+
+	/* and it should not have overwritten the previous contents */
+	igt_assert(!memcmp(&expected, &engines, idx));
+
+	memset(&engines, 0, sizeof(engines));
+	engines.class_instance[0].engine_class = I915_ENGINE_CLASS_INVALID;
+	engines.class_instance[0].engine_instance = I915_ENGINE_CLASS_INVALID_NONE;
+	idx = sizeof(*engines.class_instance);
+	p.size = base + idx;
+	gem_context_set_param(i915, &p);
+
+	memcpy(&expected, &engines, sizeof(expected));
+
+	gem_context_get_param(i915, &p);
+	igt_assert_eq(p.size, base + idx);
+	igt_assert(!memcmp(&expected, &engines, idx));
+
+	memset(&engines, 0, sizeof(engines));
+	p.size = sizeof(engines);
+	gem_context_set_param(i915, &p);
+
+	memcpy(&expected, &engines, sizeof(expected));
+
+	gem_context_get_param(i915, &p);
+	igt_assert_eq(p.size, sizeof(engines));
+	igt_assert(!memcmp(&expected, &engines, idx));
+
+	gem_context_destroy(i915, p.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("idempotent")
+		idempotent(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 e005c79c4..abfc85f46 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -110,6 +110,7 @@ i915_progs = [
 	'gem_ctx_bad_destroy',
 	'gem_ctx_clone',
 	'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] 51+ messages in thread

* [igt-dev] [PATCH i-g-t 22/25] i915: Add gem_ctx_engines
@ 2019-03-14 14:19   ` Chris Wilson
  0 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev, Andi Shyti

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 | 441 +++++++++++++++++++++++++++++++++++
 tests/meson.build            |   1 +
 3 files changed, 443 insertions(+)
 create mode 100644 tests/i915/gem_ctx_engines.c

diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index 7e06c969f..2f5612b07 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -22,6 +22,7 @@ TESTS_progs = \
 	drm_mm \
 	drm_read \
 	i915/gem_ctx_clone \
+	i915/gem_ctx_engines \
 	i915/gem_ctx_shared \
 	i915/gem_vm_create \
 	kms_3d \
diff --git a/tests/i915/gem_ctx_engines.c b/tests/i915/gem_ctx_engines.c
new file mode 100644
index 000000000..588ecf196
--- /dev/null
+++ b/tests/i915/gem_ctx_engines.c
@@ -0,0 +1,441 @@
+/*
+ * 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 idempotent(int i915)
+{
+	I915_DEFINE_CONTEXT_PARAM_ENGINES(engines , I915_EXEC_RING_MASK + 1);
+	I915_DEFINE_CONTEXT_PARAM_ENGINES(expected , I915_EXEC_RING_MASK + 1);
+	struct drm_i915_gem_context_param p = {
+		.ctx_id = gem_context_create(i915),
+		.param = I915_CONTEXT_PARAM_ENGINES,
+		.value = to_user_pointer(&engines),
+		.size = sizeof(engines),
+	};
+	const size_t base = sizeof(struct i915_context_param_engines);
+	const struct intel_execution_engine2 *e;
+	int idx;
+
+	/* What goes in, must come out. And what comes out, must go in */
+
+	gem_context_get_param(i915, &p);
+	igt_assert_eq(p.size, 0); /* atm default is to use legacy ring mask */
+
+	idx = 0;
+	memset(&engines, 0, sizeof(engines));
+	for_each_engine_class_instance(i915, e) {
+		engines.class_instance[idx].engine_class = e->class;
+		engines.class_instance[idx].engine_instance = e->instance;
+		idx++;
+	}
+	idx *= sizeof(*engines.class_instance);
+	p.size = base + idx;
+	gem_context_set_param(i915, &p);
+
+	memcpy(&expected, &engines, sizeof(expected));
+
+	gem_context_get_param(i915, &p);
+	igt_assert_eq(p.size, base + idx);
+	igt_assert(!memcmp(&expected, &engines, idx));
+
+	p.size = base;
+	gem_context_set_param(i915, &p);
+	gem_context_get_param(i915, &p);
+	igt_assert_eq(p.size, base);
+
+	/* and it should not have overwritten the previous contents */
+	igt_assert(!memcmp(&expected, &engines, idx));
+
+	memset(&engines, 0, sizeof(engines));
+	engines.class_instance[0].engine_class = I915_ENGINE_CLASS_INVALID;
+	engines.class_instance[0].engine_instance = I915_ENGINE_CLASS_INVALID_NONE;
+	idx = sizeof(*engines.class_instance);
+	p.size = base + idx;
+	gem_context_set_param(i915, &p);
+
+	memcpy(&expected, &engines, sizeof(expected));
+
+	gem_context_get_param(i915, &p);
+	igt_assert_eq(p.size, base + idx);
+	igt_assert(!memcmp(&expected, &engines, idx));
+
+	memset(&engines, 0, sizeof(engines));
+	p.size = sizeof(engines);
+	gem_context_set_param(i915, &p);
+
+	memcpy(&expected, &engines, sizeof(expected));
+
+	gem_context_get_param(i915, &p);
+	igt_assert_eq(p.size, sizeof(engines));
+	igt_assert(!memcmp(&expected, &engines, idx));
+
+	gem_context_destroy(i915, p.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("idempotent")
+		idempotent(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 e005c79c4..abfc85f46 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -110,6 +110,7 @@ i915_progs = [
 	'gem_ctx_bad_destroy',
 	'gem_ctx_clone',
 	'gem_ctx_create',
+	'gem_ctx_engines',
 	'gem_ctx_exec',
 	'gem_ctx_isolation',
 	'gem_ctx_param',
-- 
2.20.1

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

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

* [PATCH i-g-t 23/25] i915: Add gem_exec_balancer
  2019-03-14 14:19 ` [igt-dev] " Chris Wilson
@ 2019-03-14 14:19   ` Chris Wilson
  -1 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

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 | 760 +++++++++++++++++++++++++++++++++
 tests/meson.build              |   7 +
 4 files changed, 769 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 2f5612b07..8981a8ae7 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -24,6 +24,7 @@ TESTS_progs = \
 	i915/gem_ctx_clone \
 	i915/gem_ctx_engines \
 	i915/gem_ctx_shared \
+	i915/gem_exec_balancer \
 	i915/gem_vm_create \
 	kms_3d \
 	kms_addfb_basic \
diff --git a/tests/i915/gem_exec_balancer.c b/tests/i915/gem_exec_balancer.c
new file mode 100644
index 000000000..8fe6a464d
--- /dev/null
+++ b/tests/i915/gem_exec_balancer.c
@@ -0,0 +1,760 @@
+/*
+ * 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(*ci));
+
+	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 invalid_balancer(int i915)
+{
+	struct i915_context_engines_load_balance balancer = {
+		{ .name = I915_CONTEXT_ENGINES_EXT_LOAD_BALANCE },
+	};
+	I915_DEFINE_CONTEXT_PARAM_ENGINES(engines, 64);
+	struct drm_i915_gem_context_param p = {
+		.param = I915_CONTEXT_PARAM_ENGINES,
+		.value = to_user_pointer(&engines)
+	};
+	struct class_instance *ci;
+	unsigned int count;
+	uint32_t handle;
+	void *ptr;
+
+	/*
+	 * Assume that I915_CONTEXT_PARAM_ENGINE validates the array
+	 * of engines[], our job is to determine if the load_balancer
+	 * extension explodes.
+	 */
+
+	ci = list_engines(i915, ~0u, &count);
+	if (!ci)
+		return;
+
+	igt_debug("Found %d engines\n", count);
+
+	p.ctx_id = gem_context_create(i915);
+	p.size = (sizeof(struct i915_context_param_engines) +
+		  (count + 1) * sizeof(*engines.class_instance));
+
+	memset(&engines, 0, sizeof(engines));
+	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(ci));
+	gem_context_set_param(i915, &p);
+
+	engines.extensions = -1ull;
+	igt_assert_eq(__gem_context_set_param(i915, &p), -EFAULT);
+
+	engines.extensions = 1ull;
+	igt_assert_eq(__gem_context_set_param(i915, &p), -EFAULT);
+
+	engines.extensions = to_user_pointer(&balancer);
+	igt_assert_eq(__gem_context_set_param(i915, &p), -EINVAL);
+
+	balancer.engines_mask = 0x1;
+	igt_assert_eq(__gem_context_set_param(i915, &p), -EINVAL);
+
+	balancer.engines_mask = 0x2;
+	gem_context_set_param(i915, &p);
+
+	balancer.engine_index = 1;
+	igt_assert_eq(__gem_context_set_param(i915, &p), -EEXIST);
+
+	balancer.engine_index = count;
+	igt_assert_eq(__gem_context_set_param(i915, &p), -EEXIST);
+
+	balancer.engine_index = count + 1;
+	igt_assert_eq(__gem_context_set_param(i915, &p), -EINVAL);
+
+	balancer.engine_index = 0;
+	gem_context_set_param(i915, &p);
+
+	balancer.engines_mask = -1ull << (count + 1);
+	igt_assert_eq(__gem_context_set_param(i915, &p), -EINVAL);
+
+	balancer.engines_mask = 0x2;
+	gem_context_set_param(i915, &p);
+
+	balancer.base.next_extension = to_user_pointer(&balancer);
+	igt_assert_eq(__gem_context_set_param(i915, &p), -EEXIST);
+
+	balancer.base.next_extension = -1ull;
+	igt_assert_eq(__gem_context_set_param(i915, &p), -EFAULT);
+
+	handle = gem_create(i915, 4096 * 3);
+	ptr = gem_mmap__gtt(i915, handle, 4096 * 3, PROT_WRITE);
+	gem_close(i915, handle);
+
+	memset(&engines, 0, sizeof(engines));
+	engines.class_instance[0].engine_class = I915_ENGINE_CLASS_INVALID;
+	engines.class_instance[0].engine_instance = I915_ENGINE_CLASS_INVALID_NONE;
+	engines.class_instance[1].engine_class = I915_ENGINE_CLASS_INVALID;
+	engines.class_instance[1].engine_instance = I915_ENGINE_CLASS_INVALID_NONE;
+	memcpy(engines.class_instance + 2, ci, count * sizeof(ci));
+	p.size = (sizeof(struct i915_context_param_engines) +
+		  (count + 2) * sizeof(*engines.class_instance));
+	gem_context_set_param(i915, &p);
+
+	balancer.base.next_extension = 0;
+	balancer.engine_index = 1;
+	balancer.engines_mask = 0x4;
+	engines.extensions = to_user_pointer(&balancer);
+	gem_context_set_param(i915, &p);
+
+	memcpy(ptr + 4096 - 8, &balancer, sizeof(balancer));
+	memcpy(ptr + 8192 - 8, &balancer, sizeof(balancer));
+	balancer.engine_index = 0;
+
+	engines.extensions = to_user_pointer(ptr) + 4096 - 8;
+	gem_context_set_param(i915, &p);
+
+	balancer.base.next_extension = engines.extensions;
+	engines.extensions = to_user_pointer(&balancer);
+	gem_context_set_param(i915, &p);
+
+	munmap(ptr, 4096);
+	igt_assert_eq(__gem_context_set_param(i915, &p), -EFAULT);
+	engines.extensions = to_user_pointer(ptr) + 4096 - 8;
+	igt_assert_eq(__gem_context_set_param(i915, &p), -EFAULT);
+
+	engines.extensions = to_user_pointer(ptr) + 8192 - 8;
+	gem_context_set_param(i915, &p);
+
+	balancer.base.next_extension = engines.extensions;
+	engines.extensions = to_user_pointer(&balancer);
+	gem_context_set_param(i915, &p);
+
+	munmap(ptr + 8192, 4096);
+	igt_assert_eq(__gem_context_set_param(i915, &p), -EFAULT);
+	engines.extensions = to_user_pointer(ptr) + 8192 - 8;
+	igt_assert_eq(__gem_context_set_param(i915, &p), -EFAULT);
+
+	munmap(ptr + 4096, 4096);
+
+	gem_context_destroy(i915, p.ctx_id);
+	free(ci);
+}
+
+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);
+	gem_quiescent_gpu(i915);
+}
+
+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;
+		IGT_CORK_FENCE(cork);
+		unsigned int count;
+		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 = ctx);
+			} else {
+				struct drm_i915_gem_execbuffer2 eb = {
+					.buffers_ptr = spin->execbuf.buffers_ptr,
+					.buffer_count = spin->execbuf.buffer_count,
+					.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_quiescent_gpu(i915);
+	}
+
+	gem_close(i915, batch.handle);
+	gem_quiescent_gpu(i915);
+}
+
+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]);
+
+	gem_quiescent_gpu(i915);
+}
+
+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("invalid-balancer")
+		invalid_balancer(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 abfc85f46..718d3eaff 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -295,6 +295,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] 51+ messages in thread

* [igt-dev] [PATCH i-g-t 23/25] i915: Add gem_exec_balancer
@ 2019-03-14 14:19   ` Chris Wilson
  0 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

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 | 760 +++++++++++++++++++++++++++++++++
 tests/meson.build              |   7 +
 4 files changed, 769 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 2f5612b07..8981a8ae7 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -24,6 +24,7 @@ TESTS_progs = \
 	i915/gem_ctx_clone \
 	i915/gem_ctx_engines \
 	i915/gem_ctx_shared \
+	i915/gem_exec_balancer \
 	i915/gem_vm_create \
 	kms_3d \
 	kms_addfb_basic \
diff --git a/tests/i915/gem_exec_balancer.c b/tests/i915/gem_exec_balancer.c
new file mode 100644
index 000000000..8fe6a464d
--- /dev/null
+++ b/tests/i915/gem_exec_balancer.c
@@ -0,0 +1,760 @@
+/*
+ * 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(*ci));
+
+	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 invalid_balancer(int i915)
+{
+	struct i915_context_engines_load_balance balancer = {
+		{ .name = I915_CONTEXT_ENGINES_EXT_LOAD_BALANCE },
+	};
+	I915_DEFINE_CONTEXT_PARAM_ENGINES(engines, 64);
+	struct drm_i915_gem_context_param p = {
+		.param = I915_CONTEXT_PARAM_ENGINES,
+		.value = to_user_pointer(&engines)
+	};
+	struct class_instance *ci;
+	unsigned int count;
+	uint32_t handle;
+	void *ptr;
+
+	/*
+	 * Assume that I915_CONTEXT_PARAM_ENGINE validates the array
+	 * of engines[], our job is to determine if the load_balancer
+	 * extension explodes.
+	 */
+
+	ci = list_engines(i915, ~0u, &count);
+	if (!ci)
+		return;
+
+	igt_debug("Found %d engines\n", count);
+
+	p.ctx_id = gem_context_create(i915);
+	p.size = (sizeof(struct i915_context_param_engines) +
+		  (count + 1) * sizeof(*engines.class_instance));
+
+	memset(&engines, 0, sizeof(engines));
+	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(ci));
+	gem_context_set_param(i915, &p);
+
+	engines.extensions = -1ull;
+	igt_assert_eq(__gem_context_set_param(i915, &p), -EFAULT);
+
+	engines.extensions = 1ull;
+	igt_assert_eq(__gem_context_set_param(i915, &p), -EFAULT);
+
+	engines.extensions = to_user_pointer(&balancer);
+	igt_assert_eq(__gem_context_set_param(i915, &p), -EINVAL);
+
+	balancer.engines_mask = 0x1;
+	igt_assert_eq(__gem_context_set_param(i915, &p), -EINVAL);
+
+	balancer.engines_mask = 0x2;
+	gem_context_set_param(i915, &p);
+
+	balancer.engine_index = 1;
+	igt_assert_eq(__gem_context_set_param(i915, &p), -EEXIST);
+
+	balancer.engine_index = count;
+	igt_assert_eq(__gem_context_set_param(i915, &p), -EEXIST);
+
+	balancer.engine_index = count + 1;
+	igt_assert_eq(__gem_context_set_param(i915, &p), -EINVAL);
+
+	balancer.engine_index = 0;
+	gem_context_set_param(i915, &p);
+
+	balancer.engines_mask = -1ull << (count + 1);
+	igt_assert_eq(__gem_context_set_param(i915, &p), -EINVAL);
+
+	balancer.engines_mask = 0x2;
+	gem_context_set_param(i915, &p);
+
+	balancer.base.next_extension = to_user_pointer(&balancer);
+	igt_assert_eq(__gem_context_set_param(i915, &p), -EEXIST);
+
+	balancer.base.next_extension = -1ull;
+	igt_assert_eq(__gem_context_set_param(i915, &p), -EFAULT);
+
+	handle = gem_create(i915, 4096 * 3);
+	ptr = gem_mmap__gtt(i915, handle, 4096 * 3, PROT_WRITE);
+	gem_close(i915, handle);
+
+	memset(&engines, 0, sizeof(engines));
+	engines.class_instance[0].engine_class = I915_ENGINE_CLASS_INVALID;
+	engines.class_instance[0].engine_instance = I915_ENGINE_CLASS_INVALID_NONE;
+	engines.class_instance[1].engine_class = I915_ENGINE_CLASS_INVALID;
+	engines.class_instance[1].engine_instance = I915_ENGINE_CLASS_INVALID_NONE;
+	memcpy(engines.class_instance + 2, ci, count * sizeof(ci));
+	p.size = (sizeof(struct i915_context_param_engines) +
+		  (count + 2) * sizeof(*engines.class_instance));
+	gem_context_set_param(i915, &p);
+
+	balancer.base.next_extension = 0;
+	balancer.engine_index = 1;
+	balancer.engines_mask = 0x4;
+	engines.extensions = to_user_pointer(&balancer);
+	gem_context_set_param(i915, &p);
+
+	memcpy(ptr + 4096 - 8, &balancer, sizeof(balancer));
+	memcpy(ptr + 8192 - 8, &balancer, sizeof(balancer));
+	balancer.engine_index = 0;
+
+	engines.extensions = to_user_pointer(ptr) + 4096 - 8;
+	gem_context_set_param(i915, &p);
+
+	balancer.base.next_extension = engines.extensions;
+	engines.extensions = to_user_pointer(&balancer);
+	gem_context_set_param(i915, &p);
+
+	munmap(ptr, 4096);
+	igt_assert_eq(__gem_context_set_param(i915, &p), -EFAULT);
+	engines.extensions = to_user_pointer(ptr) + 4096 - 8;
+	igt_assert_eq(__gem_context_set_param(i915, &p), -EFAULT);
+
+	engines.extensions = to_user_pointer(ptr) + 8192 - 8;
+	gem_context_set_param(i915, &p);
+
+	balancer.base.next_extension = engines.extensions;
+	engines.extensions = to_user_pointer(&balancer);
+	gem_context_set_param(i915, &p);
+
+	munmap(ptr + 8192, 4096);
+	igt_assert_eq(__gem_context_set_param(i915, &p), -EFAULT);
+	engines.extensions = to_user_pointer(ptr) + 8192 - 8;
+	igt_assert_eq(__gem_context_set_param(i915, &p), -EFAULT);
+
+	munmap(ptr + 4096, 4096);
+
+	gem_context_destroy(i915, p.ctx_id);
+	free(ci);
+}
+
+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);
+	gem_quiescent_gpu(i915);
+}
+
+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;
+		IGT_CORK_FENCE(cork);
+		unsigned int count;
+		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 = ctx);
+			} else {
+				struct drm_i915_gem_execbuffer2 eb = {
+					.buffers_ptr = spin->execbuf.buffers_ptr,
+					.buffer_count = spin->execbuf.buffer_count,
+					.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_quiescent_gpu(i915);
+	}
+
+	gem_close(i915, batch.handle);
+	gem_quiescent_gpu(i915);
+}
+
+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]);
+
+	gem_quiescent_gpu(i915);
+}
+
+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("invalid-balancer")
+		invalid_balancer(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 abfc85f46..718d3eaff 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -295,6 +295,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

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

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

* [PATCH i-g-t 24/25] i915/gem_exec_balancer: Exercise bonded pairs
  2019-03-14 14:19 ` [igt-dev] " Chris Wilson
@ 2019-03-14 14:19   ` Chris Wilson
  -1 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

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 | 234 +++++++++++++++++++++++++++++++--
 1 file changed, 225 insertions(+), 9 deletions(-)

diff --git a/tests/i915/gem_exec_balancer.c b/tests/i915/gem_exec_balancer.c
index 8fe6a464d..3e4bd643c 100644
--- a/tests/i915/gem_exec_balancer.c
+++ b/tests/i915/gem_exec_balancer.c
@@ -102,12 +102,41 @@ 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)
+{
+	I915_DEFINE_CONTEXT_PARAM_ENGINES(engines, count);
+	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 = 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 +157,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 +170,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;
 }
@@ -275,6 +305,74 @@ static void invalid_balancer(int i915)
 	free(ci);
 }
 
+static void invalid_bonds(int i915)
+{
+	struct i915_context_engines_bond bonds[16];
+	I915_DEFINE_CONTEXT_PARAM_ENGINES(engines, 1);
+	struct drm_i915_gem_context_param p = {
+		.ctx_id = gem_context_create(i915),
+		.param = I915_CONTEXT_PARAM_ENGINES,
+		.value = to_user_pointer(&engines),
+		.size = sizeof(engines),
+	};
+	uint32_t handle;
+	void *ptr;
+
+	memset(&engines, 0, sizeof(engines));
+	gem_context_set_param(i915, &p);
+
+	memset(bonds, 0, sizeof(bonds));
+	for (int n = 0; n < ARRAY_SIZE(bonds); n++) {
+		bonds[n].base.name = I915_CONTEXT_ENGINES_EXT_BOND;
+		bonds[n].base.next_extension =
+			n ? to_user_pointer(&bonds[n - 1]) : 0;
+		bonds[n].sibling_mask = 0x1;
+	}
+	engines.extensions = to_user_pointer(&bonds);
+	gem_context_set_param(i915, &p);
+
+	bonds[0].base.next_extension = -1ull;
+	igt_assert_eq(__gem_context_set_param(i915, &p), -EFAULT);
+
+	bonds[0].base.next_extension = to_user_pointer(&bonds[0]);
+	igt_assert_eq(__gem_context_set_param(i915, &p), -E2BIG);
+
+	engines.extensions = to_user_pointer(&bonds[1]);
+	igt_assert_eq(__gem_context_set_param(i915, &p), -E2BIG);
+	bonds[0].base.next_extension = 0;
+	gem_context_set_param(i915, &p);
+
+	handle = gem_create(i915, 4096 * 3);
+	ptr = gem_mmap__gtt(i915, handle, 4096 * 3, PROT_WRITE);
+	gem_close(i915, handle);
+
+	memcpy(ptr + 4096, &bonds[0], sizeof(bonds[0]));
+	engines.extensions = to_user_pointer(ptr) + 4096;
+	gem_context_set_param(i915, &p);
+
+	memcpy(ptr, &bonds[0], sizeof(bonds[0]));
+	bonds[0].base.next_extension = to_user_pointer(ptr);
+	memcpy(ptr + 4096, &bonds[0], sizeof(bonds[0]));
+	gem_context_set_param(i915, &p);
+
+	munmap(ptr, 4096);
+	igt_assert_eq(__gem_context_set_param(i915, &p), -EFAULT);
+
+	bonds[0].base.next_extension = 0;
+	memcpy(ptr + 8192, &bonds[0], sizeof(bonds[0]));
+	bonds[0].base.next_extension = to_user_pointer(ptr) + 8192;
+	memcpy(ptr + 4096, &bonds[0], sizeof(bonds[0]));
+	gem_context_set_param(i915, &p);
+
+	munmap(ptr + 8192, 4096);
+	igt_assert_eq(__gem_context_set_param(i915, &p), -EFAULT);
+
+	munmap(ptr + 4096, 4096);
+	igt_assert_eq(__gem_context_set_param(i915, &p), -EFAULT);
+
+	gem_context_destroy(i915, p.ctx_id);
+}
+
 static void kick_kthreads(int period_us)
 {
 	sched_yield();
@@ -384,7 +482,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);
 		}
@@ -396,6 +494,115 @@ static void individual(int i915)
 	gem_quiescent_gpu(i915);
 }
 
+static void bonded(int i915, unsigned int flags)
+#define CORK 0x1
+{
+	struct i915_context_engines_bond bonds[16];
+	struct class_instance *master_engines;
+	uint32_t master;
+
+	/*
+	 * 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].base.name = I915_CONTEXT_ENGINES_EXT_BOND;
+		bonds[n].base.next_extension =
+			n ? to_user_pointer(&bonds[n - 1]) : 0;
+		bonds[n].sibling_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].master_class = master_engines[n].class;
+			bonds[n].master_instance = 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,
+							    .dependency = igt_cork_plug(&cork, i915));
+			}
+
+			spin = __igt_spin_batch_new(i915,
+						    .ctx = master,
+						    .engine = n,
+						    .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,
@@ -575,7 +782,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);
@@ -586,7 +793,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);
 
@@ -703,7 +910,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;
@@ -729,6 +936,9 @@ igt_main
 	igt_subtest("invalid-balancer")
 		invalid_balancer(i915);
 
+	igt_subtest("invalid-bonds")
+		invalid_bonds(i915);
+
 	igt_subtest("individual")
 		individual(i915);
 
@@ -754,6 +964,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] 51+ messages in thread

* [igt-dev] [PATCH i-g-t 24/25] i915/gem_exec_balancer: Exercise bonded pairs
@ 2019-03-14 14:19   ` Chris Wilson
  0 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

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 | 234 +++++++++++++++++++++++++++++++--
 1 file changed, 225 insertions(+), 9 deletions(-)

diff --git a/tests/i915/gem_exec_balancer.c b/tests/i915/gem_exec_balancer.c
index 8fe6a464d..3e4bd643c 100644
--- a/tests/i915/gem_exec_balancer.c
+++ b/tests/i915/gem_exec_balancer.c
@@ -102,12 +102,41 @@ 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)
+{
+	I915_DEFINE_CONTEXT_PARAM_ENGINES(engines, count);
+	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 = 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 +157,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 +170,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;
 }
@@ -275,6 +305,74 @@ static void invalid_balancer(int i915)
 	free(ci);
 }
 
+static void invalid_bonds(int i915)
+{
+	struct i915_context_engines_bond bonds[16];
+	I915_DEFINE_CONTEXT_PARAM_ENGINES(engines, 1);
+	struct drm_i915_gem_context_param p = {
+		.ctx_id = gem_context_create(i915),
+		.param = I915_CONTEXT_PARAM_ENGINES,
+		.value = to_user_pointer(&engines),
+		.size = sizeof(engines),
+	};
+	uint32_t handle;
+	void *ptr;
+
+	memset(&engines, 0, sizeof(engines));
+	gem_context_set_param(i915, &p);
+
+	memset(bonds, 0, sizeof(bonds));
+	for (int n = 0; n < ARRAY_SIZE(bonds); n++) {
+		bonds[n].base.name = I915_CONTEXT_ENGINES_EXT_BOND;
+		bonds[n].base.next_extension =
+			n ? to_user_pointer(&bonds[n - 1]) : 0;
+		bonds[n].sibling_mask = 0x1;
+	}
+	engines.extensions = to_user_pointer(&bonds);
+	gem_context_set_param(i915, &p);
+
+	bonds[0].base.next_extension = -1ull;
+	igt_assert_eq(__gem_context_set_param(i915, &p), -EFAULT);
+
+	bonds[0].base.next_extension = to_user_pointer(&bonds[0]);
+	igt_assert_eq(__gem_context_set_param(i915, &p), -E2BIG);
+
+	engines.extensions = to_user_pointer(&bonds[1]);
+	igt_assert_eq(__gem_context_set_param(i915, &p), -E2BIG);
+	bonds[0].base.next_extension = 0;
+	gem_context_set_param(i915, &p);
+
+	handle = gem_create(i915, 4096 * 3);
+	ptr = gem_mmap__gtt(i915, handle, 4096 * 3, PROT_WRITE);
+	gem_close(i915, handle);
+
+	memcpy(ptr + 4096, &bonds[0], sizeof(bonds[0]));
+	engines.extensions = to_user_pointer(ptr) + 4096;
+	gem_context_set_param(i915, &p);
+
+	memcpy(ptr, &bonds[0], sizeof(bonds[0]));
+	bonds[0].base.next_extension = to_user_pointer(ptr);
+	memcpy(ptr + 4096, &bonds[0], sizeof(bonds[0]));
+	gem_context_set_param(i915, &p);
+
+	munmap(ptr, 4096);
+	igt_assert_eq(__gem_context_set_param(i915, &p), -EFAULT);
+
+	bonds[0].base.next_extension = 0;
+	memcpy(ptr + 8192, &bonds[0], sizeof(bonds[0]));
+	bonds[0].base.next_extension = to_user_pointer(ptr) + 8192;
+	memcpy(ptr + 4096, &bonds[0], sizeof(bonds[0]));
+	gem_context_set_param(i915, &p);
+
+	munmap(ptr + 8192, 4096);
+	igt_assert_eq(__gem_context_set_param(i915, &p), -EFAULT);
+
+	munmap(ptr + 4096, 4096);
+	igt_assert_eq(__gem_context_set_param(i915, &p), -EFAULT);
+
+	gem_context_destroy(i915, p.ctx_id);
+}
+
 static void kick_kthreads(int period_us)
 {
 	sched_yield();
@@ -384,7 +482,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);
 		}
@@ -396,6 +494,115 @@ static void individual(int i915)
 	gem_quiescent_gpu(i915);
 }
 
+static void bonded(int i915, unsigned int flags)
+#define CORK 0x1
+{
+	struct i915_context_engines_bond bonds[16];
+	struct class_instance *master_engines;
+	uint32_t master;
+
+	/*
+	 * 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].base.name = I915_CONTEXT_ENGINES_EXT_BOND;
+		bonds[n].base.next_extension =
+			n ? to_user_pointer(&bonds[n - 1]) : 0;
+		bonds[n].sibling_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].master_class = master_engines[n].class;
+			bonds[n].master_instance = 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,
+							    .dependency = igt_cork_plug(&cork, i915));
+			}
+
+			spin = __igt_spin_batch_new(i915,
+						    .ctx = master,
+						    .engine = n,
+						    .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,
@@ -575,7 +782,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);
@@ -586,7 +793,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);
 
@@ -703,7 +910,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;
@@ -729,6 +936,9 @@ igt_main
 	igt_subtest("invalid-balancer")
 		invalid_balancer(i915);
 
+	igt_subtest("invalid-bonds")
+		invalid_bonds(i915);
+
 	igt_subtest("individual")
 		individual(i915);
 
@@ -754,6 +964,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

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

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

* [PATCH i-g-t 25/25] i915/gem_exec_latency: Measure the latency of context switching
  2019-03-14 14:19 ` [igt-dev] " Chris Wilson
@ 2019-03-14 14:19   ` Chris Wilson
  -1 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

Measure the baseline latency between contexts in order to directly
compare that with the additional cost of preemption.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 tests/i915/gem_ctx_shared.c   |   2 +-
 tests/i915/gem_exec_latency.c | 142 ++++++++++++++++++++++++++++++++++
 2 files changed, 143 insertions(+), 1 deletion(-)

diff --git a/tests/i915/gem_ctx_shared.c b/tests/i915/gem_ctx_shared.c
index 426155356..187921c7e 100644
--- a/tests/i915/gem_ctx_shared.c
+++ b/tests/i915/gem_ctx_shared.c
@@ -595,7 +595,7 @@ static void independent(int i915, unsigned ring, unsigned flags)
 		break;
 
 	default:
-		igt_skip("mmio base not known");
+		igt_skip("mmio base not known\n");
 	}
 
 	for (int n = 0; n < ARRAY_SIZE(spin); n++) {
diff --git a/tests/i915/gem_exec_latency.c b/tests/i915/gem_exec_latency.c
index 6dd191ece..89c5af647 100644
--- a/tests/i915/gem_exec_latency.c
+++ b/tests/i915/gem_exec_latency.c
@@ -628,6 +628,139 @@ rthog_latency_on_ring(int fd, unsigned int engine, const char *name, unsigned in
 	munmap(results, MMAP_SZ);
 }
 
+static void context_switch(int i915,
+			   unsigned int engine, const char *name,
+			   unsigned int flags)
+{
+	struct drm_i915_gem_exec_object2 obj[2];
+	struct drm_i915_gem_relocation_entry reloc[5];
+	struct drm_i915_gem_execbuffer2 eb;
+	uint32_t *cs, *bbe, *results, v;
+	unsigned int mmio_base;
+	struct igt_mean mean;
+	uint32_t ctx[2];
+
+	/* XXX i915_query()! */
+	igt_skip_on(intel_gen(intel_get_drm_devid(i915)) >= 11);
+	switch (engine) {
+	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 i = 0; i < ARRAY_SIZE(ctx); i++)
+		ctx[i] = gem_context_create(i915);
+
+	if (flags & PREEMPT) {
+		gem_context_set_priority(i915, ctx[0], -1023);
+		gem_context_set_priority(i915, ctx[1], +1023);
+	}
+
+	memset(obj, 0, sizeof(obj));
+	obj[0].handle = gem_create(i915, 4096);
+	gem_set_caching(i915, obj[0].handle, 1);
+	results = gem_mmap__cpu(i915, obj[0].handle, 0, 4096, PROT_READ);
+	gem_set_domain(i915, obj[0].handle, I915_GEM_DOMAIN_CPU, 0);
+
+	obj[1].handle = gem_create(i915, 4096);
+	memset(reloc,0, sizeof(reloc));
+	obj[1].relocation_count = ARRAY_SIZE(reloc);
+	obj[1].relocs_ptr = to_user_pointer(reloc);
+	bbe = gem_mmap__wc(i915, obj[1].handle, 0, 4096, PROT_WRITE);
+	gem_set_domain(i915, obj[1].handle,
+		       I915_GEM_DOMAIN_WC, I915_GEM_DOMAIN_WC);
+
+	cs = bbe;
+	*cs++ = 0x5 << 23;
+	*cs++ = 0x24 << 23 | 2; /* SRM */
+	*cs++ = mmio_base + 0x358; /* TIMESTAMP */
+	reloc[0].target_handle = obj[0].handle;
+	reloc[0].offset = (cs - bbe) * sizeof(*cs);
+	*cs++ = 0;
+	*cs++ = 0;
+	*cs++ = MI_BATCH_BUFFER_START | 1;
+	reloc[1].target_handle = obj[1].handle;
+	reloc[1].offset = (cs - bbe) * sizeof(*cs);
+	*cs++ = 0;
+	*cs++ = 0;
+
+	cs = bbe + 64;
+	*cs++ = 0x24 << 23 | 2; /* SRM */
+	*cs++ = mmio_base + 0x358; /* TIMESTAMP */
+	reloc[2].target_handle = obj[0].handle;
+	reloc[2].offset = (cs - bbe) * sizeof(*cs);
+	*cs++ = reloc[2].delta = 4;
+	*cs++ = 0;
+	*cs++ = 0x29 << 23 | 2; /* LRM */
+	*cs++ = mmio_base + 0x600; /* GPR0 */
+	reloc[3].target_handle = obj[0].handle;
+	reloc[3].offset = (cs - bbe) * sizeof(*cs);
+	*cs++ = 0;
+	*cs++ = 0;
+	*cs++ = 0x24 << 23 | 2; /* SRM */
+	*cs++ = mmio_base + 0x600; /* GPR0 */
+	reloc[4].target_handle = obj[0].handle;
+	reloc[4].offset = (cs - bbe) * sizeof(*cs);
+	*cs++ = reloc[4].delta = 8;
+	*cs++ = 0;
+	*cs++ = 0xa << 23;
+
+	memset(&eb, 0, sizeof(eb));
+	eb.buffers_ptr = to_user_pointer(obj);
+	eb.buffer_count = ARRAY_SIZE(obj);
+	eb.flags = engine;
+	eb.flags |= LOCAL_I915_EXEC_NO_RELOC;
+
+	v = 0;
+	igt_mean_init(&mean);
+	igt_until_timeout(5) {
+		eb.rsvd1 = ctx[0];
+		eb.batch_start_offset = 0;
+		gem_execbuf(i915, &eb);
+
+		while (results[0] == v)
+			igt_assert(gem_bo_busy(i915, obj[1].handle));
+
+		eb.rsvd1 = ctx[1];
+		eb.batch_start_offset = 64 * sizeof(*cs);
+		gem_execbuf(i915, &eb);
+
+		*bbe = 0xa << 23;
+		gem_sync(i915, obj[1].handle);
+		*bbe = 0x5 << 23;
+
+		v = results[0];
+		igt_mean_add(&mean, (results[1] - results[2]) * rcs_clock);
+	}
+	igt_info("%s context switch latency%s: %.2f±%.2fus\n",
+		 name, flags & PREEMPT ? " (preempt)" : "",
+		 1e-3 * igt_mean_get(&mean),
+		 1e-3 * sqrt(igt_mean_get_variance(&mean)));
+	munmap(results, 4096);
+	munmap(bbe, 4096);
+
+	for (int i = 0; i < ARRAY_SIZE(obj); i++)
+		gem_close(i915, obj[i].handle);
+
+	for (int i = 0; i < ARRAY_SIZE(ctx); i++)
+		gem_context_destroy(i915, ctx[i]);
+}
+
 static double clockrate(int i915, int reg)
 {
 	volatile uint32_t *mmio;
@@ -753,12 +886,21 @@ igt_main
 							  e->exec_id | e->flags,
 							  e->name, CORK);
 
+				igt_subtest_f("%s-cs", e->name)
+					context_switch(device,
+						       e->exec_id | e->flags,
+						       e->name, 0);
 				igt_subtest_group {
 					igt_fixture {
 						gem_require_contexts(device);
 						igt_require(gem_scheduler_has_preemption(device));
 					}
 
+					igt_subtest_f("%s-cs-preempt", e->name)
+						context_switch(device,
+								e->exec_id | e->flags,
+								e->name, PREEMPT);
+
 					igt_subtest_f("%s-preemption", e->name)
 						latency_from_ring(device,
 								  e->exec_id | e->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] 51+ messages in thread

* [Intel-gfx] [PATCH i-g-t 25/25] i915/gem_exec_latency: Measure the latency of context switching
@ 2019-03-14 14:19   ` Chris Wilson
  0 siblings, 0 replies; 51+ messages in thread
From: Chris Wilson @ 2019-03-14 14:19 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

Measure the baseline latency between contexts in order to directly
compare that with the additional cost of preemption.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 tests/i915/gem_ctx_shared.c   |   2 +-
 tests/i915/gem_exec_latency.c | 142 ++++++++++++++++++++++++++++++++++
 2 files changed, 143 insertions(+), 1 deletion(-)

diff --git a/tests/i915/gem_ctx_shared.c b/tests/i915/gem_ctx_shared.c
index 426155356..187921c7e 100644
--- a/tests/i915/gem_ctx_shared.c
+++ b/tests/i915/gem_ctx_shared.c
@@ -595,7 +595,7 @@ static void independent(int i915, unsigned ring, unsigned flags)
 		break;
 
 	default:
-		igt_skip("mmio base not known");
+		igt_skip("mmio base not known\n");
 	}
 
 	for (int n = 0; n < ARRAY_SIZE(spin); n++) {
diff --git a/tests/i915/gem_exec_latency.c b/tests/i915/gem_exec_latency.c
index 6dd191ece..89c5af647 100644
--- a/tests/i915/gem_exec_latency.c
+++ b/tests/i915/gem_exec_latency.c
@@ -628,6 +628,139 @@ rthog_latency_on_ring(int fd, unsigned int engine, const char *name, unsigned in
 	munmap(results, MMAP_SZ);
 }
 
+static void context_switch(int i915,
+			   unsigned int engine, const char *name,
+			   unsigned int flags)
+{
+	struct drm_i915_gem_exec_object2 obj[2];
+	struct drm_i915_gem_relocation_entry reloc[5];
+	struct drm_i915_gem_execbuffer2 eb;
+	uint32_t *cs, *bbe, *results, v;
+	unsigned int mmio_base;
+	struct igt_mean mean;
+	uint32_t ctx[2];
+
+	/* XXX i915_query()! */
+	igt_skip_on(intel_gen(intel_get_drm_devid(i915)) >= 11);
+	switch (engine) {
+	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 i = 0; i < ARRAY_SIZE(ctx); i++)
+		ctx[i] = gem_context_create(i915);
+
+	if (flags & PREEMPT) {
+		gem_context_set_priority(i915, ctx[0], -1023);
+		gem_context_set_priority(i915, ctx[1], +1023);
+	}
+
+	memset(obj, 0, sizeof(obj));
+	obj[0].handle = gem_create(i915, 4096);
+	gem_set_caching(i915, obj[0].handle, 1);
+	results = gem_mmap__cpu(i915, obj[0].handle, 0, 4096, PROT_READ);
+	gem_set_domain(i915, obj[0].handle, I915_GEM_DOMAIN_CPU, 0);
+
+	obj[1].handle = gem_create(i915, 4096);
+	memset(reloc,0, sizeof(reloc));
+	obj[1].relocation_count = ARRAY_SIZE(reloc);
+	obj[1].relocs_ptr = to_user_pointer(reloc);
+	bbe = gem_mmap__wc(i915, obj[1].handle, 0, 4096, PROT_WRITE);
+	gem_set_domain(i915, obj[1].handle,
+		       I915_GEM_DOMAIN_WC, I915_GEM_DOMAIN_WC);
+
+	cs = bbe;
+	*cs++ = 0x5 << 23;
+	*cs++ = 0x24 << 23 | 2; /* SRM */
+	*cs++ = mmio_base + 0x358; /* TIMESTAMP */
+	reloc[0].target_handle = obj[0].handle;
+	reloc[0].offset = (cs - bbe) * sizeof(*cs);
+	*cs++ = 0;
+	*cs++ = 0;
+	*cs++ = MI_BATCH_BUFFER_START | 1;
+	reloc[1].target_handle = obj[1].handle;
+	reloc[1].offset = (cs - bbe) * sizeof(*cs);
+	*cs++ = 0;
+	*cs++ = 0;
+
+	cs = bbe + 64;
+	*cs++ = 0x24 << 23 | 2; /* SRM */
+	*cs++ = mmio_base + 0x358; /* TIMESTAMP */
+	reloc[2].target_handle = obj[0].handle;
+	reloc[2].offset = (cs - bbe) * sizeof(*cs);
+	*cs++ = reloc[2].delta = 4;
+	*cs++ = 0;
+	*cs++ = 0x29 << 23 | 2; /* LRM */
+	*cs++ = mmio_base + 0x600; /* GPR0 */
+	reloc[3].target_handle = obj[0].handle;
+	reloc[3].offset = (cs - bbe) * sizeof(*cs);
+	*cs++ = 0;
+	*cs++ = 0;
+	*cs++ = 0x24 << 23 | 2; /* SRM */
+	*cs++ = mmio_base + 0x600; /* GPR0 */
+	reloc[4].target_handle = obj[0].handle;
+	reloc[4].offset = (cs - bbe) * sizeof(*cs);
+	*cs++ = reloc[4].delta = 8;
+	*cs++ = 0;
+	*cs++ = 0xa << 23;
+
+	memset(&eb, 0, sizeof(eb));
+	eb.buffers_ptr = to_user_pointer(obj);
+	eb.buffer_count = ARRAY_SIZE(obj);
+	eb.flags = engine;
+	eb.flags |= LOCAL_I915_EXEC_NO_RELOC;
+
+	v = 0;
+	igt_mean_init(&mean);
+	igt_until_timeout(5) {
+		eb.rsvd1 = ctx[0];
+		eb.batch_start_offset = 0;
+		gem_execbuf(i915, &eb);
+
+		while (results[0] == v)
+			igt_assert(gem_bo_busy(i915, obj[1].handle));
+
+		eb.rsvd1 = ctx[1];
+		eb.batch_start_offset = 64 * sizeof(*cs);
+		gem_execbuf(i915, &eb);
+
+		*bbe = 0xa << 23;
+		gem_sync(i915, obj[1].handle);
+		*bbe = 0x5 << 23;
+
+		v = results[0];
+		igt_mean_add(&mean, (results[1] - results[2]) * rcs_clock);
+	}
+	igt_info("%s context switch latency%s: %.2f±%.2fus\n",
+		 name, flags & PREEMPT ? " (preempt)" : "",
+		 1e-3 * igt_mean_get(&mean),
+		 1e-3 * sqrt(igt_mean_get_variance(&mean)));
+	munmap(results, 4096);
+	munmap(bbe, 4096);
+
+	for (int i = 0; i < ARRAY_SIZE(obj); i++)
+		gem_close(i915, obj[i].handle);
+
+	for (int i = 0; i < ARRAY_SIZE(ctx); i++)
+		gem_context_destroy(i915, ctx[i]);
+}
+
 static double clockrate(int i915, int reg)
 {
 	volatile uint32_t *mmio;
@@ -753,12 +886,21 @@ igt_main
 							  e->exec_id | e->flags,
 							  e->name, CORK);
 
+				igt_subtest_f("%s-cs", e->name)
+					context_switch(device,
+						       e->exec_id | e->flags,
+						       e->name, 0);
 				igt_subtest_group {
 					igt_fixture {
 						gem_require_contexts(device);
 						igt_require(gem_scheduler_has_preemption(device));
 					}
 
+					igt_subtest_f("%s-cs-preempt", e->name)
+						context_switch(device,
+								e->exec_id | e->flags,
+								e->name, PREEMPT);
+
 					igt_subtest_f("%s-preemption", e->name)
 						latency_from_ring(device,
 								  e->exec_id | e->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] 51+ messages in thread

* [igt-dev] ✓ Fi.CI.BAT: success for series starting with [i-g-t,01/25] i915/gem_create: Always try to create an object of at least one page
  2019-03-14 14:19 ` [igt-dev] " Chris Wilson
                   ` (24 preceding siblings ...)
  (?)
@ 2019-03-14 15:01 ` Patchwork
  -1 siblings, 0 replies; 51+ messages in thread
From: Patchwork @ 2019-03-14 15:01 UTC (permalink / raw)
  To: Chris Wilson; +Cc: igt-dev

== Series Details ==

Series: series starting with [i-g-t,01/25] i915/gem_create: Always try to create an object of at least one page
URL   : https://patchwork.freedesktop.org/series/58004/
State : success

== Summary ==

CI Bug Log - changes from CI_DRM_5745 -> IGTPW_2623
====================================================

Summary
-------

  **SUCCESS**

  No regressions found.

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

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

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

### IGT changes ###

#### Issues hit ####

  * igt@amdgpu/amd_basic@cs-sdma:
    - fi-skl-6770hq:      NOTRUN -> SKIP [fdo#109271] +37

  * igt@amdgpu/amd_cs_nop@fork-compute0:
    - fi-blb-e6850:       NOTRUN -> SKIP [fdo#109271] +18

  * igt@amdgpu/amd_cs_nop@sync-compute0:
    - fi-bdw-gvtdvm:      NOTRUN -> SKIP [fdo#109271] +42

  * igt@amdgpu/amd_cs_nop@sync-fork-compute0:
    - fi-icl-u3:          NOTRUN -> SKIP [fdo#109315] +17

  * igt@amdgpu/amd_prime@amd-to-i915:
    - fi-kbl-x1275:       NOTRUN -> SKIP [fdo#109271] +45

  * igt@gem_ctx_create@basic-files:
    - fi-gdg-551:         NOTRUN -> SKIP [fdo#109271] +106

  * igt@gem_exec_basic@gtt-bsd:
    - fi-bwr-2160:        NOTRUN -> SKIP [fdo#109271] +103

  * igt@gem_exec_basic@gtt-bsd1:
    - fi-icl-u3:          NOTRUN -> SKIP [fdo#109276] +7

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

  * igt@gem_exec_gttfill@basic:
    - fi-skl-gvtdvm:      NOTRUN -> SKIP [fdo#109271] +41

  * igt@gem_exec_parse@basic-rejected:
    - fi-icl-u3:          NOTRUN -> SKIP [fdo#109289] +1

  * igt@gem_exec_store@basic-bsd1:
    - fi-kbl-r:           NOTRUN -> SKIP [fdo#109271] +41

  * igt@i915_pm_rpm@basic-pci-d3-state:
    - fi-bsw-kefka:       PASS -> SKIP [fdo#109271]

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

  * igt@i915_pm_rpm@module-reload:
    - fi-skl-6770hq:      NOTRUN -> FAIL [fdo#108511]

  * igt@i915_selftest@live_contexts:
    - fi-icl-u3:          NOTRUN -> DMESG-FAIL [fdo#108569]

  * igt@i915_selftest@live_execlists:
    - fi-apl-guc:         PASS -> INCOMPLETE [fdo#103927] / [fdo#109720]

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

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

  * igt@kms_busy@basic-flip-c:
    - fi-bwr-2160:        NOTRUN -> SKIP [fdo#109271] / [fdo#109278]
    - fi-byt-clapper:     NOTRUN -> SKIP [fdo#109271] / [fdo#109278]
    - fi-gdg-551:         NOTRUN -> SKIP [fdo#109271] / [fdo#109278]
    - fi-byt-n2820:       NOTRUN -> SKIP [fdo#109271] / [fdo#109278]

  * igt@kms_chamelium@common-hpd-after-suspend:
    - fi-kbl-7567u:       PASS -> WARN [fdo#109380]

  * igt@kms_chamelium@hdmi-edid-read:
    - fi-icl-u3:          NOTRUN -> SKIP [fdo#109284] +8

  * igt@kms_chamelium@vga-edid-read:
    - fi-hsw-4770r:       NOTRUN -> SKIP [fdo#109271] +45

  * igt@kms_force_connector_basic@prune-stale-modes:
    - fi-icl-u3:          NOTRUN -> SKIP [fdo#109285] +3

  * igt@kms_frontbuffer_tracking@basic:
    - fi-icl-u3:          NOTRUN -> FAIL [fdo#103167]
    - fi-byt-clapper:     NOTRUN -> FAIL [fdo#103167]

  * igt@kms_pipe_crc_basic@nonblocking-crc-pipe-c:
    - fi-kbl-7567u:       PASS -> SKIP [fdo#109271] +33

  * igt@runner@aborted:
    - fi-bxt-dsi:         NOTRUN -> FAIL [fdo#109516]
    - fi-apl-guc:         NOTRUN -> FAIL [fdo#108622] / [fdo#109720]

  
#### Possible fixes ####

  * igt@i915_module_load@reload:
    - fi-blb-e6850:       INCOMPLETE [fdo#107718] -> PASS

  * igt@i915_pm_rpm@basic-pci-d3-state:
    - fi-byt-j1900:       SKIP [fdo#109271] -> PASS

  * igt@i915_pm_rpm@basic-rte:
    - fi-byt-j1900:       FAIL [fdo#108800] -> PASS

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

  [fdo#103167]: https://bugs.freedesktop.org/show_bug.cgi?id=103167
  [fdo#103182]: https://bugs.freedesktop.org/show_bug.cgi?id=103182
  [fdo#103927]: https://bugs.freedesktop.org/show_bug.cgi?id=103927
  [fdo#107718]: https://bugs.freedesktop.org/show_bug.cgi?id=107718
  [fdo#108511]: https://bugs.freedesktop.org/show_bug.cgi?id=108511
  [fdo#108569]: https://bugs.freedesktop.org/show_bug.cgi?id=108569
  [fdo#108622]: https://bugs.freedesktop.org/show_bug.cgi?id=108622
  [fdo#108800]: https://bugs.freedesktop.org/show_bug.cgi?id=108800
  [fdo#109271]: https://bugs.freedesktop.org/show_bug.cgi?id=109271
  [fdo#109276]: https://bugs.freedesktop.org/show_bug.cgi?id=109276
  [fdo#109278]: https://bugs.freedesktop.org/show_bug.cgi?id=109278
  [fdo#109284]: https://bugs.freedesktop.org/show_bug.cgi?id=109284
  [fdo#109285]: https://bugs.freedesktop.org/show_bug.cgi?id=109285
  [fdo#109289]: https://bugs.freedesktop.org/show_bug.cgi?id=109289
  [fdo#109294]: https://bugs.freedesktop.org/show_bug.cgi?id=109294
  [fdo#109315]: https://bugs.freedesktop.org/show_bug.cgi?id=109315
  [fdo#109380]: https://bugs.freedesktop.org/show_bug.cgi?id=109380
  [fdo#109516]: https://bugs.freedesktop.org/show_bug.cgi?id=109516
  [fdo#109720]: https://bugs.freedesktop.org/show_bug.cgi?id=109720
  [fdo#110028]: https://bugs.freedesktop.org/show_bug.cgi?id=110028


Participating hosts (35 -> 41)
------------------------------

  Additional (13): fi-hsw-4770r fi-bxt-dsi fi-skl-gvtdvm fi-skl-6770hq fi-bdw-gvtdvm fi-bwr-2160 fi-kbl-x1275 fi-gdg-551 fi-icl-u3 fi-icl-y fi-byt-n2820 fi-byt-clapper fi-kbl-r 
  Missing    (7): fi-kbl-soraka fi-ilk-m540 fi-hsw-4200u fi-byt-squawks fi-ctg-p8600 fi-bdw-samus fi-skl-6600u 


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

    * IGT: IGT_4884 -> IGTPW_2623

  CI_DRM_5745: 170699448ee836d2a24fc3b75fb331682eea7887 @ git://anongit.freedesktop.org/gfx-ci/linux
  IGTPW_2623: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_2623/
  IGT_4884: c46051337b972f8b5a302afb6f603df06fea527d @ git://anongit.freedesktop.org/xorg/app/intel-gpu-tools



== Testlist changes ==

+++ 231 lines
--- 4 lines

== Logs ==

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

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

end of thread, other threads:[~2019-03-14 15:01 UTC | newest]

Thread overview: 51+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-03-14 14:19 [PATCH i-g-t 01/25] i915/gem_create: Always try to create an object of at least one page Chris Wilson
2019-03-14 14:19 ` [igt-dev] " Chris Wilson
2019-03-14 14:19 ` [PATCH i-g-t 02/25] lib/i915: Pretty print HW semaphores Chris Wilson
2019-03-14 14:19   ` [igt-dev] " Chris Wilson
2019-03-14 14:19 ` [PATCH i-g-t 03/25] lib: Add GPU power measurement Chris Wilson
2019-03-14 14:19   ` [igt-dev] " Chris Wilson
2019-03-14 14:19 ` [PATCH i-g-t 04/25] i915/gem_exec_schedule: Measure semaphore power consumption Chris Wilson
2019-03-14 14:19   ` [igt-dev] " Chris Wilson
2019-03-14 14:19 ` [PATCH i-g-t 05/25] i915/gem_exec_whisper: Measure total power consumed Chris Wilson
2019-03-14 14:19   ` [igt-dev] " Chris Wilson
2019-03-14 14:19 ` [PATCH i-g-t 06/25] i915/gem_exec_schedule: Verify that using HW semaphores doesn't block Chris Wilson
2019-03-14 14:19   ` [igt-dev] " Chris Wilson
2019-03-14 14:19 ` [PATCH i-g-t 07/25] i915/gem_exec_nop: poll-sequential requires ordering between rings Chris Wilson
2019-03-14 14:19   ` [igt-dev] " Chris Wilson
2019-03-14 14:19 ` [PATCH i-g-t 08/25] i915/gem_sync: Make switch-default asymmetric Chris Wilson
2019-03-14 14:19   ` [igt-dev] " Chris Wilson
2019-03-14 14:19 ` [PATCH i-g-t 09/25] i915/gem_ctx_param: Remove kneecapping Chris Wilson
2019-03-14 14:19   ` [igt-dev] " Chris Wilson
2019-03-14 14:19 ` [PATCH i-g-t 10/25] i915/gem_exec_big: Add a single shot test Chris Wilson
2019-03-14 14:19   ` [igt-dev] " Chris Wilson
2019-03-14 14:19 ` [PATCH i-g-t 11/25] kms_fence_pin_leak: Ask for the GPU before use Chris Wilson
2019-03-14 14:19   ` [Intel-gfx] " Chris Wilson
2019-03-14 14:19 ` [PATCH i-g-t 12/25] drm-uapi: Import i915_drm.h upto 364df3d04d51 Chris Wilson
2019-03-14 14:19   ` [igt-dev] " Chris Wilson
2019-03-14 14:19 ` [PATCH i-g-t 13/25] lib/i915: Improve gem_context error messages Chris Wilson
2019-03-14 14:19   ` [igt-dev] " Chris Wilson
2019-03-14 14:19 ` [PATCH i-g-t 14/25] i915/gem_ctx_param: Test set/get (copy) VM Chris Wilson
2019-03-14 14:19   ` [igt-dev] " Chris Wilson
2019-03-14 14:19 ` [PATCH i-g-t 15/25] i915/gem_ctx_create: Basic checks for constructor properties Chris Wilson
2019-03-14 14:19   ` [igt-dev] " Chris Wilson
2019-03-14 14:19 ` [PATCH i-g-t 16/25] i915: Add gem_ctx_clone Chris Wilson
2019-03-14 14:19   ` [igt-dev] " Chris Wilson
2019-03-14 14:19 ` [PATCH i-g-t 17/25] i915: Add gem_vm_create Chris Wilson
2019-03-14 14:19   ` [Intel-gfx] " Chris Wilson
2019-03-14 14:19 ` [PATCH i-g-t 18/25] i915: Exercise creating context with shared GTT Chris Wilson
2019-03-14 14:19   ` [igt-dev] " Chris Wilson
2019-03-14 14:19 ` [PATCH i-g-t 19/25] i915/gem_ctx_switch: Exercise queues Chris Wilson
2019-03-14 14:19   ` [igt-dev] " Chris Wilson
2019-03-14 14:19 ` [PATCH i-g-t 20/25] i915/gem_exec_whisper: Fork all-engine tests one-per-engine Chris Wilson
2019-03-14 14:19   ` [igt-dev] " Chris Wilson
2019-03-14 14:19 ` [PATCH i-g-t 21/25] i915/gem_exec_whisper: debugfs/next_seqno is defunct Chris Wilson
2019-03-14 14:19   ` [Intel-gfx] " Chris Wilson
2019-03-14 14:19 ` [PATCH i-g-t 22/25] i915: Add gem_ctx_engines Chris Wilson
2019-03-14 14:19   ` [igt-dev] " Chris Wilson
2019-03-14 14:19 ` [PATCH i-g-t 23/25] i915: Add gem_exec_balancer Chris Wilson
2019-03-14 14:19   ` [igt-dev] " Chris Wilson
2019-03-14 14:19 ` [PATCH i-g-t 24/25] i915/gem_exec_balancer: Exercise bonded pairs Chris Wilson
2019-03-14 14:19   ` [igt-dev] " Chris Wilson
2019-03-14 14:19 ` [PATCH i-g-t 25/25] i915/gem_exec_latency: Measure the latency of context switching Chris Wilson
2019-03-14 14:19   ` [Intel-gfx] " Chris Wilson
2019-03-14 15:01 ` [igt-dev] ✓ Fi.CI.BAT: success for series starting with [i-g-t,01/25] i915/gem_create: Always try to create an object of at least one page Patchwork

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.