All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH igt] igt: Add gem_ctx_freq to exercise requesting freq on a ctx
@ 2018-03-07 22:49 ` Chris Wilson
  0 siblings, 0 replies; 36+ messages in thread
From: Chris Wilson @ 2018-03-07 22:49 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

Exercise some new API that allows applications to request that
individual contexts are executed within a desired frequency range.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 tests/Makefile.am      |   2 +-
 tests/Makefile.sources |   1 +
 tests/gem_ctx_freq.c   | 190 +++++++++++++++++++++++++++++++++++++++++++++++++
 tests/meson.build      |   1 +
 4 files changed, 193 insertions(+), 1 deletion(-)
 create mode 100644 tests/gem_ctx_freq.c

diff --git a/tests/Makefile.am b/tests/Makefile.am
index edd689a4..f42641f6 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -104,6 +104,7 @@ drm_import_export_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 drm_import_export_LDADD = $(LDADD) -lpthread
 gem_close_race_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_close_race_LDADD = $(LDADD) -lpthread
+gem_ctx_freq_LDADD = $(LDADD) $(top_builddir)/lib/libigt_perf.la
 gem_ctx_thrash_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_ctx_thrash_LDADD = $(LDADD) -lpthread
 gem_exec_parallel_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
@@ -128,7 +129,6 @@ prime_self_import_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 prime_self_import_LDADD = $(LDADD) -lpthread
 gem_userptr_blits_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_userptr_blits_LDADD = $(LDADD) -lpthread
-perf_pmu_LDADD = $(LDADD) $(top_builddir)/lib/libigt_perf.la
 
 gem_wait_LDADD = $(LDADD) -lrt
 kms_flip_LDADD = $(LDADD) -lrt -lpthread
diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index 05cdc1ef..06e729ef 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -59,6 +59,7 @@ TESTS_progs = \
 	gem_ctx_bad_exec \
 	gem_ctx_create \
 	gem_ctx_exec \
+	gem_ctx_freq \
 	gem_ctx_isolation \
 	gem_ctx_param \
 	gem_ctx_shared \
diff --git a/tests/gem_ctx_freq.c b/tests/gem_ctx_freq.c
new file mode 100644
index 00000000..a01ce01b
--- /dev/null
+++ b/tests/gem_ctx_freq.c
@@ -0,0 +1,190 @@
+/*
+ * 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 <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 <time.h>
+
+#include "igt.h"
+#include "igt_perf.h"
+
+#define LOCAL_CONTEXT_PARAM_FREQUENCY 8
+
+static void set_freq(int fd, uint32_t ctx, uint32_t min, uint32_t max)
+{
+	struct drm_i915_gem_context_param param = {
+		.ctx_id = ctx,
+		.param = LOCAL_CONTEXT_PARAM_FREQUENCY,
+		.value = (uint64_t)max << 32 | min,
+	};
+
+	gem_context_set_param(fd, &param);
+}
+
+static void get_freq(int fd, uint32_t ctx, uint32_t *min, uint32_t *max)
+{
+	struct drm_i915_gem_context_param param = {
+		.ctx_id = ctx,
+		.param = LOCAL_CONTEXT_PARAM_FREQUENCY,
+	};
+
+	gem_context_get_param(fd, &param);
+
+	*min = param.value & 0xffffffff;
+	*max = param.value >> 32;
+}
+
+static double measure_frequency(int pmu, int delay)
+{
+	uint64_t data[2];
+	uint64_t d_t, d_v;
+
+	igt_assert_eq(read(pmu, data, sizeof(data)), sizeof(data));
+	d_v = -data[0];
+	d_t = -data[1];
+
+	usleep(delay);
+
+	igt_assert_eq(read(pmu, data, sizeof(data)), sizeof(data));
+	d_v += data[0];
+	d_t += data[1];
+
+	return d_v * 1e9 / d_t;
+}
+
+static void single(int fd, const struct intel_execution_engine *e)
+{
+	const unsigned int engine = e->exec_id | e->flags;
+	uint32_t ctx = gem_context_create(fd);
+	uint32_t min, max;
+	double measured;
+	igt_spin_t *spin;
+	int pmu;
+
+	get_freq(fd, ctx, &min, &max);
+	igt_info("Min freq: %dMHz; Max freq: %dMHz\n", min, max);
+
+	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
+	igt_require(pmu >= 0);
+
+	gem_quiescent_gpu(fd);
+	measured = measure_frequency(pmu, 10000);
+	igt_info("Initial (idle) freq: %.1fMHz\n",measured);
+	igt_require(measured >= min - 50 && measured <= min + 50);
+
+	for (uint32_t freq = min + 50; freq <= max; freq += 100) {
+		set_freq(fd, ctx, freq, freq);
+
+		gem_quiescent_gpu(fd);
+		spin = __igt_spin_batch_new(fd, ctx, engine, 0);
+		usleep(10000);
+
+		measured = measure_frequency(pmu, 50000);
+		igt_debugfs_dump(fd, "i915_rps_boost_info");
+
+		igt_spin_batch_free(fd, spin);
+		igt_info("%s(single): Measured %.1fMHz, expected %dMhz\n",
+			 e->name, measured, freq);
+		igt_assert(measured > freq - 100 && measured < freq + 100);
+	}
+	gem_quiescent_gpu(fd);
+
+	spin = __igt_spin_batch_new(fd, ctx, engine, 0);
+	for (uint32_t freq = min + 50; freq <= max; freq += 100) {
+		igt_spin_t *kick;
+
+		set_freq(fd, ctx, freq, freq);
+
+		/*
+		 * When requesting a new frequency on the currently
+		 * executing context, it does not take effect until the
+		 * next context switch. In this case, we trigger a lite
+		 * restore.
+		 */
+		kick = __igt_spin_batch_new(fd, ctx, engine, 0);
+		igt_spin_batch_free(fd, spin);
+		spin = kick;
+
+		usleep(10000);
+
+		measured = measure_frequency(pmu, 50000);
+		igt_debugfs_dump(fd, "i915_rps_boost_info");
+
+		igt_info("%s(continuous): Measured %.1fMHz, expected %dMhz\n",
+			 e->name, measured, freq);
+		igt_assert(measured > freq - 100 && measured < freq + 100);
+	}
+	igt_spin_batch_free(fd, spin);
+
+	gem_quiescent_gpu(fd);
+	measured = measure_frequency(pmu, 10000);
+	igt_info("Final (idle) freq: %.1fMHz\n", measured);
+	igt_assert(measured >= min - 50 && measured <= min + 50);
+
+	close(pmu);
+	gem_context_destroy(fd, ctx);
+}
+
+static bool has_ctx_freq(int fd)
+{
+	struct drm_i915_gem_context_param param = {
+		.param = LOCAL_CONTEXT_PARAM_FREQUENCY,
+	};
+
+	return __gem_context_get_param(fd, &param) == 0;
+}
+
+igt_main
+{
+	const struct intel_execution_engine *e;
+	int fd = -1;
+
+	igt_fixture {
+		fd = drm_open_driver(DRIVER_INTEL);
+		igt_require_gem(fd);
+
+		igt_require(has_ctx_freq(fd));
+	}
+
+	igt_skip_on_simulation();
+
+	for (e = intel_execution_engines; e->name; e++) {
+		if (e->exec_id == 0)
+			continue;
+
+		igt_subtest_f("%s%s", e->exec_id == 0 ? "basic-" : "", e->name) {
+			igt_require(gem_ring_has_physical_engine(fd, e->exec_id | e->flags));
+			single(fd, e);
+		}
+	}
+}
diff --git a/tests/meson.build b/tests/meson.build
index 58729231..f1271274 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -35,6 +35,7 @@ test_progs = [
 	'gem_ctx_bad_exec',
 	'gem_ctx_create',
 	'gem_ctx_exec',
+	'gem_ctx_freq',
 	'gem_ctx_param',
 	'gem_ctx_switch',
 	'gem_ctx_thrash',
-- 
2.16.2

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

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

* [igt-dev] [PATCH igt] igt: Add gem_ctx_freq to exercise requesting freq on a ctx
@ 2018-03-07 22:49 ` Chris Wilson
  0 siblings, 0 replies; 36+ messages in thread
From: Chris Wilson @ 2018-03-07 22:49 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

Exercise some new API that allows applications to request that
individual contexts are executed within a desired frequency range.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 tests/Makefile.am      |   2 +-
 tests/Makefile.sources |   1 +
 tests/gem_ctx_freq.c   | 190 +++++++++++++++++++++++++++++++++++++++++++++++++
 tests/meson.build      |   1 +
 4 files changed, 193 insertions(+), 1 deletion(-)
 create mode 100644 tests/gem_ctx_freq.c

diff --git a/tests/Makefile.am b/tests/Makefile.am
index edd689a4..f42641f6 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -104,6 +104,7 @@ drm_import_export_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 drm_import_export_LDADD = $(LDADD) -lpthread
 gem_close_race_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_close_race_LDADD = $(LDADD) -lpthread
+gem_ctx_freq_LDADD = $(LDADD) $(top_builddir)/lib/libigt_perf.la
 gem_ctx_thrash_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_ctx_thrash_LDADD = $(LDADD) -lpthread
 gem_exec_parallel_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
@@ -128,7 +129,6 @@ prime_self_import_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 prime_self_import_LDADD = $(LDADD) -lpthread
 gem_userptr_blits_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_userptr_blits_LDADD = $(LDADD) -lpthread
-perf_pmu_LDADD = $(LDADD) $(top_builddir)/lib/libigt_perf.la
 
 gem_wait_LDADD = $(LDADD) -lrt
 kms_flip_LDADD = $(LDADD) -lrt -lpthread
diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index 05cdc1ef..06e729ef 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -59,6 +59,7 @@ TESTS_progs = \
 	gem_ctx_bad_exec \
 	gem_ctx_create \
 	gem_ctx_exec \
+	gem_ctx_freq \
 	gem_ctx_isolation \
 	gem_ctx_param \
 	gem_ctx_shared \
diff --git a/tests/gem_ctx_freq.c b/tests/gem_ctx_freq.c
new file mode 100644
index 00000000..a01ce01b
--- /dev/null
+++ b/tests/gem_ctx_freq.c
@@ -0,0 +1,190 @@
+/*
+ * 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 <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 <time.h>
+
+#include "igt.h"
+#include "igt_perf.h"
+
+#define LOCAL_CONTEXT_PARAM_FREQUENCY 8
+
+static void set_freq(int fd, uint32_t ctx, uint32_t min, uint32_t max)
+{
+	struct drm_i915_gem_context_param param = {
+		.ctx_id = ctx,
+		.param = LOCAL_CONTEXT_PARAM_FREQUENCY,
+		.value = (uint64_t)max << 32 | min,
+	};
+
+	gem_context_set_param(fd, &param);
+}
+
+static void get_freq(int fd, uint32_t ctx, uint32_t *min, uint32_t *max)
+{
+	struct drm_i915_gem_context_param param = {
+		.ctx_id = ctx,
+		.param = LOCAL_CONTEXT_PARAM_FREQUENCY,
+	};
+
+	gem_context_get_param(fd, &param);
+
+	*min = param.value & 0xffffffff;
+	*max = param.value >> 32;
+}
+
+static double measure_frequency(int pmu, int delay)
+{
+	uint64_t data[2];
+	uint64_t d_t, d_v;
+
+	igt_assert_eq(read(pmu, data, sizeof(data)), sizeof(data));
+	d_v = -data[0];
+	d_t = -data[1];
+
+	usleep(delay);
+
+	igt_assert_eq(read(pmu, data, sizeof(data)), sizeof(data));
+	d_v += data[0];
+	d_t += data[1];
+
+	return d_v * 1e9 / d_t;
+}
+
+static void single(int fd, const struct intel_execution_engine *e)
+{
+	const unsigned int engine = e->exec_id | e->flags;
+	uint32_t ctx = gem_context_create(fd);
+	uint32_t min, max;
+	double measured;
+	igt_spin_t *spin;
+	int pmu;
+
+	get_freq(fd, ctx, &min, &max);
+	igt_info("Min freq: %dMHz; Max freq: %dMHz\n", min, max);
+
+	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
+	igt_require(pmu >= 0);
+
+	gem_quiescent_gpu(fd);
+	measured = measure_frequency(pmu, 10000);
+	igt_info("Initial (idle) freq: %.1fMHz\n",measured);
+	igt_require(measured >= min - 50 && measured <= min + 50);
+
+	for (uint32_t freq = min + 50; freq <= max; freq += 100) {
+		set_freq(fd, ctx, freq, freq);
+
+		gem_quiescent_gpu(fd);
+		spin = __igt_spin_batch_new(fd, ctx, engine, 0);
+		usleep(10000);
+
+		measured = measure_frequency(pmu, 50000);
+		igt_debugfs_dump(fd, "i915_rps_boost_info");
+
+		igt_spin_batch_free(fd, spin);
+		igt_info("%s(single): Measured %.1fMHz, expected %dMhz\n",
+			 e->name, measured, freq);
+		igt_assert(measured > freq - 100 && measured < freq + 100);
+	}
+	gem_quiescent_gpu(fd);
+
+	spin = __igt_spin_batch_new(fd, ctx, engine, 0);
+	for (uint32_t freq = min + 50; freq <= max; freq += 100) {
+		igt_spin_t *kick;
+
+		set_freq(fd, ctx, freq, freq);
+
+		/*
+		 * When requesting a new frequency on the currently
+		 * executing context, it does not take effect until the
+		 * next context switch. In this case, we trigger a lite
+		 * restore.
+		 */
+		kick = __igt_spin_batch_new(fd, ctx, engine, 0);
+		igt_spin_batch_free(fd, spin);
+		spin = kick;
+
+		usleep(10000);
+
+		measured = measure_frequency(pmu, 50000);
+		igt_debugfs_dump(fd, "i915_rps_boost_info");
+
+		igt_info("%s(continuous): Measured %.1fMHz, expected %dMhz\n",
+			 e->name, measured, freq);
+		igt_assert(measured > freq - 100 && measured < freq + 100);
+	}
+	igt_spin_batch_free(fd, spin);
+
+	gem_quiescent_gpu(fd);
+	measured = measure_frequency(pmu, 10000);
+	igt_info("Final (idle) freq: %.1fMHz\n", measured);
+	igt_assert(measured >= min - 50 && measured <= min + 50);
+
+	close(pmu);
+	gem_context_destroy(fd, ctx);
+}
+
+static bool has_ctx_freq(int fd)
+{
+	struct drm_i915_gem_context_param param = {
+		.param = LOCAL_CONTEXT_PARAM_FREQUENCY,
+	};
+
+	return __gem_context_get_param(fd, &param) == 0;
+}
+
+igt_main
+{
+	const struct intel_execution_engine *e;
+	int fd = -1;
+
+	igt_fixture {
+		fd = drm_open_driver(DRIVER_INTEL);
+		igt_require_gem(fd);
+
+		igt_require(has_ctx_freq(fd));
+	}
+
+	igt_skip_on_simulation();
+
+	for (e = intel_execution_engines; e->name; e++) {
+		if (e->exec_id == 0)
+			continue;
+
+		igt_subtest_f("%s%s", e->exec_id == 0 ? "basic-" : "", e->name) {
+			igt_require(gem_ring_has_physical_engine(fd, e->exec_id | e->flags));
+			single(fd, e);
+		}
+	}
+}
diff --git a/tests/meson.build b/tests/meson.build
index 58729231..f1271274 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -35,6 +35,7 @@ test_progs = [
 	'gem_ctx_bad_exec',
 	'gem_ctx_create',
 	'gem_ctx_exec',
+	'gem_ctx_freq',
 	'gem_ctx_param',
 	'gem_ctx_switch',
 	'gem_ctx_thrash',
-- 
2.16.2

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

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

* [igt-dev] ✗ Fi.CI.BAT: failure for igt: Add gem_ctx_freq to exercise requesting freq on a ctx
  2018-03-07 22:49 ` [igt-dev] " Chris Wilson
  (?)
@ 2018-03-07 23:00 ` Patchwork
  -1 siblings, 0 replies; 36+ messages in thread
From: Patchwork @ 2018-03-07 23:00 UTC (permalink / raw)
  To: Chris Wilson; +Cc: igt-dev

== Series Details ==

Series: igt: Add gem_ctx_freq to exercise requesting freq on a ctx
URL   : https://patchwork.freedesktop.org/series/39564/
State : failure

== Summary ==

Applying: igt: Add gem_ctx_freq to exercise requesting freq on a ctx
Patch failed at 0001 igt: Add gem_ctx_freq to exercise requesting freq on a ctx
The copy of the patch that failed is found in: .git/rebase-apply/patch
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".

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

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

* [PATCH igt] igt: Add gem_ctx_freq to exercise requesting freq on a ctx
  2018-03-07 22:49 ` [igt-dev] " Chris Wilson
@ 2018-03-08  0:13   ` Chris Wilson
  -1 siblings, 0 replies; 36+ messages in thread
From: Chris Wilson @ 2018-03-08  0:13 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

Exercise some new API that allows applications to request that
individual contexts are executed within a desired frequency range.

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

diff --git a/tests/Makefile.am b/tests/Makefile.am
index dbc7be72..389f7fc7 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -104,6 +104,7 @@ drm_import_export_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 drm_import_export_LDADD = $(LDADD) -lpthread
 gem_close_race_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_close_race_LDADD = $(LDADD) -lpthread
+gem_ctx_freq_LDADD = $(LDADD) $(top_builddir)/lib/libigt_perf.la
 gem_ctx_thrash_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_ctx_thrash_LDADD = $(LDADD) -lpthread
 gem_exec_parallel_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index 4a81ac4a..3d079c42 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -58,6 +58,7 @@ TESTS_progs = \
 	gem_ctx_bad_exec \
 	gem_ctx_create \
 	gem_ctx_exec \
+	gem_ctx_freq \
 	gem_ctx_isolation \
 	gem_ctx_param \
 	gem_ctx_switch \
diff --git a/tests/gem_ctx_freq.c b/tests/gem_ctx_freq.c
new file mode 100644
index 00000000..e68d9dd9
--- /dev/null
+++ b/tests/gem_ctx_freq.c
@@ -0,0 +1,338 @@
+/*
+ * 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 <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 <time.h>
+
+#include "igt.h"
+#include "igt_perf.h"
+
+#define LOCAL_CONTEXT_PARAM_FREQUENCY 8
+
+static int __set_freq(int fd, uint32_t ctx, uint32_t min, uint32_t max)
+{
+	struct drm_i915_gem_context_param param = {
+		.ctx_id = ctx,
+		.param = LOCAL_CONTEXT_PARAM_FREQUENCY,
+		.value = (uint64_t)max << 32 | min,
+	};
+
+	return __gem_context_set_param(fd, &param);
+}
+
+static void set_freq(int fd, uint32_t ctx, uint32_t min, uint32_t max)
+{
+	igt_assert_eq(__set_freq(fd, ctx, min, max), 0);
+}
+
+static void get_freq(int fd, uint32_t ctx, uint32_t *min, uint32_t *max)
+{
+	struct drm_i915_gem_context_param param = {
+		.ctx_id = ctx,
+		.param = LOCAL_CONTEXT_PARAM_FREQUENCY,
+	};
+
+	gem_context_get_param(fd, &param);
+
+	*min = param.value & 0xffffffff;
+	*max = param.value >> 32;
+}
+
+static double measure_frequency(int pmu, int delay)
+{
+	uint64_t data[2];
+	uint64_t d_t, d_v;
+
+	igt_assert_eq(read(pmu, data, sizeof(data)), sizeof(data));
+	d_v = -data[0];
+	d_t = -data[1];
+
+	usleep(delay);
+
+	igt_assert_eq(read(pmu, data, sizeof(data)), sizeof(data));
+	d_v += data[0];
+	d_t += data[1];
+
+	return d_v * 1e9 / d_t;
+}
+
+static void single(int fd, const struct intel_execution_engine *e)
+{
+	const unsigned int engine = e->exec_id | e->flags;
+	uint32_t ctx = gem_context_create(fd);
+	uint32_t min, max;
+	double measured;
+	igt_spin_t *spin;
+	int pmu;
+
+	get_freq(fd, ctx, &min, &max);
+	igt_info("Min freq: %dMHz; Max freq: %dMHz\n", min, max);
+
+	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
+	igt_require(pmu >= 0);
+
+	gem_quiescent_gpu(fd);
+	measured = measure_frequency(pmu, 10000);
+	igt_info("Initial (idle) freq: %.1fMHz\n",measured);
+	igt_require(measured >= min - 50 && measured <= min + 50);
+
+	for (uint32_t freq = min + 50; freq <= max; freq += 100) {
+		set_freq(fd, ctx, freq, freq);
+
+		gem_quiescent_gpu(fd);
+		spin = __igt_spin_batch_new(fd, ctx, engine, 0);
+		usleep(10000);
+
+		measured = measure_frequency(pmu, 50000);
+		igt_debugfs_dump(fd, "i915_rps_boost_info");
+
+		igt_spin_batch_free(fd, spin);
+		igt_info("%s(single): Measured %.1fMHz, expected %dMhz\n",
+			 e->name, measured, freq);
+		igt_assert(measured > freq - 100 && measured < freq + 100);
+	}
+	gem_quiescent_gpu(fd);
+
+	spin = __igt_spin_batch_new(fd, ctx, engine, 0);
+	for (uint32_t freq = min + 50; freq <= max; freq += 100) {
+		igt_spin_t *kick;
+
+		set_freq(fd, ctx, freq, freq);
+
+		/*
+		 * When requesting a new frequency on the currently
+		 * executing context, it does not take effect until the
+		 * next context switch. In this case, we trigger a lite
+		 * restore.
+		 */
+		kick = __igt_spin_batch_new(fd, ctx, engine, 0);
+		igt_spin_batch_free(fd, spin);
+		spin = kick;
+
+		usleep(10000);
+
+		measured = measure_frequency(pmu, 50000);
+		igt_debugfs_dump(fd, "i915_rps_boost_info");
+
+		igt_info("%s(continuous): Measured %.1fMHz, expected %dMhz\n",
+			 e->name, measured, freq);
+		igt_assert(measured > freq - 100 && measured < freq + 100);
+	}
+	igt_spin_batch_free(fd, spin);
+
+	gem_quiescent_gpu(fd);
+	measured = measure_frequency(pmu, 10000);
+	igt_info("Final (idle) freq: %.1fMHz\n", measured);
+	igt_assert(measured >= min - 50 && measured <= min + 50);
+
+	close(pmu);
+	gem_context_destroy(fd, ctx);
+}
+
+static void sandwich(int fd)
+{
+	uint32_t ctx = gem_context_create(fd);
+	unsigned int engine;
+	uint32_t min, max;
+	igt_spin_t *spin;
+	int pmu;
+
+	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
+	igt_require(pmu >= 0);
+
+	spin = igt_spin_batch_new(fd, ctx, 0, 0);
+	get_freq(fd, ctx, &min, &max);
+	set_freq(fd, ctx, min, min);
+	for_each_physical_engine(fd, engine) {
+		struct drm_i915_gem_exec_object2 obj = {
+			.handle = spin->handle,
+		};
+		struct drm_i915_gem_execbuffer2 eb = {
+			.buffer_count = 1,
+			.buffers_ptr = to_user_pointer(&obj),
+			.flags = engine,
+			.rsvd1 = ctx,
+		};
+		double measured;
+
+		min += 50;
+		if (min > max)
+			break;
+
+		set_freq(fd, ctx, min, min);
+		gem_execbuf(fd, &eb);
+		usleep(10000);
+
+		measured = measure_frequency(pmu, 50000);
+		igt_debugfs_dump(fd, "i915_rps_boost_info");
+
+		igt_info("Measured %.1fMHz, expected %dMhz\n", measured, min);
+		igt_assert(measured > min - 100 && measured < min + 100);
+	}
+	igt_spin_batch_free(fd, spin);
+	gem_quiescent_gpu(fd);
+
+	gem_context_destroy(fd, ctx);
+	close(pmu);
+}
+
+static void invalid_param(int fd)
+{
+	uint32_t min, max;
+	uint32_t cur_min, cur_max;
+
+	get_freq(fd, 0, &min, &max);
+
+	igt_assert_eq(__set_freq(fd, 0, min - 50, max), -EINVAL);
+	igt_assert_eq(__set_freq(fd, 0, min, max + 50), -EINVAL);
+	igt_assert_eq(__set_freq(fd, 0, min + 50, min), -EINVAL);
+	igt_assert_eq(__set_freq(fd, 0, max, max - 50), -EINVAL);
+
+	get_freq(fd, 0, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+}
+
+static void idempotent(int fd)
+{
+	uint32_t min, max;
+	uint32_t cur_min, cur_max;
+
+	get_freq(fd, 0, &min, &max);
+
+	set_freq(fd, 0, max, max);
+	get_freq(fd, 0, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, max);
+	igt_assert_eq(cur_max, max);
+
+	set_freq(fd, 0, min, min);
+	get_freq(fd, 0, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, min);
+
+	set_freq(fd, 0, min, max);
+	get_freq(fd, 0, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+}
+
+static void independent(int fd)
+{
+	uint32_t min, max;
+	uint32_t cur_min, cur_max;
+	uint32_t ctx[2];
+
+	get_freq(fd, 0, &min, &max);
+
+	set_freq(fd, 0, max, max);
+	ctx[0] = gem_context_create(fd);
+	get_freq(fd, ctx[0], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+
+	set_freq(fd, 0, min, min);
+	get_freq(fd, ctx[0], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+
+	ctx[1] = gem_context_create(fd);
+	get_freq(fd, ctx[1], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+
+	set_freq(fd, ctx[1], max, max);
+	get_freq(fd, ctx[0], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+
+	get_freq(fd, 0, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, min);
+
+	get_freq(fd, ctx[1], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, max);
+	igt_assert_eq(cur_max, max);
+	gem_context_destroy(fd, ctx[1]);
+
+	get_freq(fd, ctx[0], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+	gem_context_destroy(fd, ctx[0]);
+}
+
+static bool has_ctx_freq(int fd)
+{
+	struct drm_i915_gem_context_param param = {
+		.param = LOCAL_CONTEXT_PARAM_FREQUENCY,
+	};
+
+	return __gem_context_get_param(fd, &param) == 0;
+}
+
+igt_main
+{
+	const struct intel_execution_engine *e;
+	int fd = -1;
+
+	igt_fixture {
+		fd = drm_open_driver(DRIVER_INTEL);
+		igt_require_gem(fd);
+
+		igt_require(has_ctx_freq(fd));
+	}
+
+	igt_subtest("invalid")
+		invalid_param(fd);
+
+	igt_subtest("idempotent")
+		idempotent(fd);
+
+	igt_subtest("independent")
+		independent(fd);
+
+	igt_skip_on_simulation();
+
+	for (e = intel_execution_engines; e->name; e++) {
+		if (e->exec_id == 0)
+			continue;
+
+		igt_subtest(e->name) {
+			igt_require(gem_ring_has_physical_engine(fd, e->exec_id | e->flags));
+			single(fd, e);
+		}
+	}
+
+	igt_subtest("sandwich")
+		sandwich(fd);
+}
diff --git a/tests/meson.build b/tests/meson.build
index 58729231..f1271274 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -35,6 +35,7 @@ test_progs = [
 	'gem_ctx_bad_exec',
 	'gem_ctx_create',
 	'gem_ctx_exec',
+	'gem_ctx_freq',
 	'gem_ctx_param',
 	'gem_ctx_switch',
 	'gem_ctx_thrash',
-- 
2.16.2

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

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

* [igt-dev] [PATCH igt] igt: Add gem_ctx_freq to exercise requesting freq on a ctx
@ 2018-03-08  0:13   ` Chris Wilson
  0 siblings, 0 replies; 36+ messages in thread
From: Chris Wilson @ 2018-03-08  0:13 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev

Exercise some new API that allows applications to request that
individual contexts are executed within a desired frequency range.

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

diff --git a/tests/Makefile.am b/tests/Makefile.am
index dbc7be72..389f7fc7 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -104,6 +104,7 @@ drm_import_export_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 drm_import_export_LDADD = $(LDADD) -lpthread
 gem_close_race_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_close_race_LDADD = $(LDADD) -lpthread
+gem_ctx_freq_LDADD = $(LDADD) $(top_builddir)/lib/libigt_perf.la
 gem_ctx_thrash_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_ctx_thrash_LDADD = $(LDADD) -lpthread
 gem_exec_parallel_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index 4a81ac4a..3d079c42 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -58,6 +58,7 @@ TESTS_progs = \
 	gem_ctx_bad_exec \
 	gem_ctx_create \
 	gem_ctx_exec \
+	gem_ctx_freq \
 	gem_ctx_isolation \
 	gem_ctx_param \
 	gem_ctx_switch \
diff --git a/tests/gem_ctx_freq.c b/tests/gem_ctx_freq.c
new file mode 100644
index 00000000..e68d9dd9
--- /dev/null
+++ b/tests/gem_ctx_freq.c
@@ -0,0 +1,338 @@
+/*
+ * 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 <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 <time.h>
+
+#include "igt.h"
+#include "igt_perf.h"
+
+#define LOCAL_CONTEXT_PARAM_FREQUENCY 8
+
+static int __set_freq(int fd, uint32_t ctx, uint32_t min, uint32_t max)
+{
+	struct drm_i915_gem_context_param param = {
+		.ctx_id = ctx,
+		.param = LOCAL_CONTEXT_PARAM_FREQUENCY,
+		.value = (uint64_t)max << 32 | min,
+	};
+
+	return __gem_context_set_param(fd, &param);
+}
+
+static void set_freq(int fd, uint32_t ctx, uint32_t min, uint32_t max)
+{
+	igt_assert_eq(__set_freq(fd, ctx, min, max), 0);
+}
+
+static void get_freq(int fd, uint32_t ctx, uint32_t *min, uint32_t *max)
+{
+	struct drm_i915_gem_context_param param = {
+		.ctx_id = ctx,
+		.param = LOCAL_CONTEXT_PARAM_FREQUENCY,
+	};
+
+	gem_context_get_param(fd, &param);
+
+	*min = param.value & 0xffffffff;
+	*max = param.value >> 32;
+}
+
+static double measure_frequency(int pmu, int delay)
+{
+	uint64_t data[2];
+	uint64_t d_t, d_v;
+
+	igt_assert_eq(read(pmu, data, sizeof(data)), sizeof(data));
+	d_v = -data[0];
+	d_t = -data[1];
+
+	usleep(delay);
+
+	igt_assert_eq(read(pmu, data, sizeof(data)), sizeof(data));
+	d_v += data[0];
+	d_t += data[1];
+
+	return d_v * 1e9 / d_t;
+}
+
+static void single(int fd, const struct intel_execution_engine *e)
+{
+	const unsigned int engine = e->exec_id | e->flags;
+	uint32_t ctx = gem_context_create(fd);
+	uint32_t min, max;
+	double measured;
+	igt_spin_t *spin;
+	int pmu;
+
+	get_freq(fd, ctx, &min, &max);
+	igt_info("Min freq: %dMHz; Max freq: %dMHz\n", min, max);
+
+	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
+	igt_require(pmu >= 0);
+
+	gem_quiescent_gpu(fd);
+	measured = measure_frequency(pmu, 10000);
+	igt_info("Initial (idle) freq: %.1fMHz\n",measured);
+	igt_require(measured >= min - 50 && measured <= min + 50);
+
+	for (uint32_t freq = min + 50; freq <= max; freq += 100) {
+		set_freq(fd, ctx, freq, freq);
+
+		gem_quiescent_gpu(fd);
+		spin = __igt_spin_batch_new(fd, ctx, engine, 0);
+		usleep(10000);
+
+		measured = measure_frequency(pmu, 50000);
+		igt_debugfs_dump(fd, "i915_rps_boost_info");
+
+		igt_spin_batch_free(fd, spin);
+		igt_info("%s(single): Measured %.1fMHz, expected %dMhz\n",
+			 e->name, measured, freq);
+		igt_assert(measured > freq - 100 && measured < freq + 100);
+	}
+	gem_quiescent_gpu(fd);
+
+	spin = __igt_spin_batch_new(fd, ctx, engine, 0);
+	for (uint32_t freq = min + 50; freq <= max; freq += 100) {
+		igt_spin_t *kick;
+
+		set_freq(fd, ctx, freq, freq);
+
+		/*
+		 * When requesting a new frequency on the currently
+		 * executing context, it does not take effect until the
+		 * next context switch. In this case, we trigger a lite
+		 * restore.
+		 */
+		kick = __igt_spin_batch_new(fd, ctx, engine, 0);
+		igt_spin_batch_free(fd, spin);
+		spin = kick;
+
+		usleep(10000);
+
+		measured = measure_frequency(pmu, 50000);
+		igt_debugfs_dump(fd, "i915_rps_boost_info");
+
+		igt_info("%s(continuous): Measured %.1fMHz, expected %dMhz\n",
+			 e->name, measured, freq);
+		igt_assert(measured > freq - 100 && measured < freq + 100);
+	}
+	igt_spin_batch_free(fd, spin);
+
+	gem_quiescent_gpu(fd);
+	measured = measure_frequency(pmu, 10000);
+	igt_info("Final (idle) freq: %.1fMHz\n", measured);
+	igt_assert(measured >= min - 50 && measured <= min + 50);
+
+	close(pmu);
+	gem_context_destroy(fd, ctx);
+}
+
+static void sandwich(int fd)
+{
+	uint32_t ctx = gem_context_create(fd);
+	unsigned int engine;
+	uint32_t min, max;
+	igt_spin_t *spin;
+	int pmu;
+
+	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
+	igt_require(pmu >= 0);
+
+	spin = igt_spin_batch_new(fd, ctx, 0, 0);
+	get_freq(fd, ctx, &min, &max);
+	set_freq(fd, ctx, min, min);
+	for_each_physical_engine(fd, engine) {
+		struct drm_i915_gem_exec_object2 obj = {
+			.handle = spin->handle,
+		};
+		struct drm_i915_gem_execbuffer2 eb = {
+			.buffer_count = 1,
+			.buffers_ptr = to_user_pointer(&obj),
+			.flags = engine,
+			.rsvd1 = ctx,
+		};
+		double measured;
+
+		min += 50;
+		if (min > max)
+			break;
+
+		set_freq(fd, ctx, min, min);
+		gem_execbuf(fd, &eb);
+		usleep(10000);
+
+		measured = measure_frequency(pmu, 50000);
+		igt_debugfs_dump(fd, "i915_rps_boost_info");
+
+		igt_info("Measured %.1fMHz, expected %dMhz\n", measured, min);
+		igt_assert(measured > min - 100 && measured < min + 100);
+	}
+	igt_spin_batch_free(fd, spin);
+	gem_quiescent_gpu(fd);
+
+	gem_context_destroy(fd, ctx);
+	close(pmu);
+}
+
+static void invalid_param(int fd)
+{
+	uint32_t min, max;
+	uint32_t cur_min, cur_max;
+
+	get_freq(fd, 0, &min, &max);
+
+	igt_assert_eq(__set_freq(fd, 0, min - 50, max), -EINVAL);
+	igt_assert_eq(__set_freq(fd, 0, min, max + 50), -EINVAL);
+	igt_assert_eq(__set_freq(fd, 0, min + 50, min), -EINVAL);
+	igt_assert_eq(__set_freq(fd, 0, max, max - 50), -EINVAL);
+
+	get_freq(fd, 0, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+}
+
+static void idempotent(int fd)
+{
+	uint32_t min, max;
+	uint32_t cur_min, cur_max;
+
+	get_freq(fd, 0, &min, &max);
+
+	set_freq(fd, 0, max, max);
+	get_freq(fd, 0, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, max);
+	igt_assert_eq(cur_max, max);
+
+	set_freq(fd, 0, min, min);
+	get_freq(fd, 0, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, min);
+
+	set_freq(fd, 0, min, max);
+	get_freq(fd, 0, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+}
+
+static void independent(int fd)
+{
+	uint32_t min, max;
+	uint32_t cur_min, cur_max;
+	uint32_t ctx[2];
+
+	get_freq(fd, 0, &min, &max);
+
+	set_freq(fd, 0, max, max);
+	ctx[0] = gem_context_create(fd);
+	get_freq(fd, ctx[0], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+
+	set_freq(fd, 0, min, min);
+	get_freq(fd, ctx[0], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+
+	ctx[1] = gem_context_create(fd);
+	get_freq(fd, ctx[1], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+
+	set_freq(fd, ctx[1], max, max);
+	get_freq(fd, ctx[0], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+
+	get_freq(fd, 0, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, min);
+
+	get_freq(fd, ctx[1], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, max);
+	igt_assert_eq(cur_max, max);
+	gem_context_destroy(fd, ctx[1]);
+
+	get_freq(fd, ctx[0], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+	gem_context_destroy(fd, ctx[0]);
+}
+
+static bool has_ctx_freq(int fd)
+{
+	struct drm_i915_gem_context_param param = {
+		.param = LOCAL_CONTEXT_PARAM_FREQUENCY,
+	};
+
+	return __gem_context_get_param(fd, &param) == 0;
+}
+
+igt_main
+{
+	const struct intel_execution_engine *e;
+	int fd = -1;
+
+	igt_fixture {
+		fd = drm_open_driver(DRIVER_INTEL);
+		igt_require_gem(fd);
+
+		igt_require(has_ctx_freq(fd));
+	}
+
+	igt_subtest("invalid")
+		invalid_param(fd);
+
+	igt_subtest("idempotent")
+		idempotent(fd);
+
+	igt_subtest("independent")
+		independent(fd);
+
+	igt_skip_on_simulation();
+
+	for (e = intel_execution_engines; e->name; e++) {
+		if (e->exec_id == 0)
+			continue;
+
+		igt_subtest(e->name) {
+			igt_require(gem_ring_has_physical_engine(fd, e->exec_id | e->flags));
+			single(fd, e);
+		}
+	}
+
+	igt_subtest("sandwich")
+		sandwich(fd);
+}
diff --git a/tests/meson.build b/tests/meson.build
index 58729231..f1271274 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -35,6 +35,7 @@ test_progs = [
 	'gem_ctx_bad_exec',
 	'gem_ctx_create',
 	'gem_ctx_exec',
+	'gem_ctx_freq',
 	'gem_ctx_param',
 	'gem_ctx_switch',
 	'gem_ctx_thrash',
-- 
2.16.2

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

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

* [igt-dev] ✓ Fi.CI.BAT: success for igt: Add gem_ctx_freq to exercise requesting freq on a ctx (rev2)
  2018-03-07 22:49 ` [igt-dev] " Chris Wilson
                   ` (2 preceding siblings ...)
  (?)
@ 2018-03-08  0:42 ` Patchwork
  -1 siblings, 0 replies; 36+ messages in thread
From: Patchwork @ 2018-03-08  0:42 UTC (permalink / raw)
  To: Chris Wilson; +Cc: igt-dev

== Series Details ==

Series: igt: Add gem_ctx_freq to exercise requesting freq on a ctx (rev2)
URL   : https://patchwork.freedesktop.org/series/39564/
State : success

== Summary ==

IGT patchset tested on top of latest successful build
b4689dce36d0fbd9aec70d5a4b077c43a6b9c254 igt: Remove gen7_forcewake_mt

with latest DRM-Tip kernel build CI_DRM_3892
fc93d196b8e4 drm-tip: 2018y-03m-07d-23h-55m-21s UTC integration manifest

Testlist changes:
+igt@gem_ctx_freq@blt
+igt@gem_ctx_freq@bsd
+igt@gem_ctx_freq@bsd1
+igt@gem_ctx_freq@bsd2
+igt@gem_ctx_freq@idempotent
+igt@gem_ctx_freq@independent
+igt@gem_ctx_freq@invalid
+igt@gem_ctx_freq@render
+igt@gem_ctx_freq@sandwich
+igt@gem_ctx_freq@vebox

---- Known issues:

Test kms_pipe_crc_basic:
        Subgroup suspend-read-crc-pipe-b:
                pass       -> INCOMPLETE (fi-snb-2520m) fdo#103713

fdo#103713 https://bugs.freedesktop.org/show_bug.cgi?id=103713

fi-bdw-5557u     total:288  pass:267  dwarn:0   dfail:0   fail:0   skip:21  time:425s
fi-bdw-gvtdvm    total:288  pass:264  dwarn:0   dfail:0   fail:0   skip:24  time:431s
fi-blb-e6850     total:288  pass:223  dwarn:1   dfail:0   fail:0   skip:64  time:377s
fi-bsw-n3050     total:288  pass:242  dwarn:0   dfail:0   fail:0   skip:46  time:514s
fi-bwr-2160      total:288  pass:183  dwarn:0   dfail:0   fail:0   skip:105 time:284s
fi-bxt-dsi       total:288  pass:258  dwarn:0   dfail:0   fail:0   skip:30  time:490s
fi-bxt-j4205     total:288  pass:259  dwarn:0   dfail:0   fail:0   skip:29  time:495s
fi-byt-j1900     total:288  pass:253  dwarn:0   dfail:0   fail:0   skip:35  time:484s
fi-byt-n2820     total:288  pass:249  dwarn:0   dfail:0   fail:0   skip:39  time:471s
fi-cfl-8700k     total:288  pass:260  dwarn:0   dfail:0   fail:0   skip:28  time:406s
fi-cfl-s2        total:288  pass:262  dwarn:0   dfail:0   fail:0   skip:26  time:582s
fi-cfl-u         total:288  pass:262  dwarn:0   dfail:0   fail:0   skip:26  time:510s
fi-elk-e7500     total:288  pass:229  dwarn:0   dfail:0   fail:0   skip:59  time:418s
fi-gdg-551       total:288  pass:179  dwarn:0   dfail:0   fail:1   skip:108 time:290s
fi-glk-1         total:288  pass:260  dwarn:0   dfail:0   fail:0   skip:28  time:518s
fi-hsw-4770      total:288  pass:261  dwarn:0   dfail:0   fail:0   skip:27  time:397s
fi-ilk-650       total:288  pass:228  dwarn:0   dfail:0   fail:0   skip:60  time:413s
fi-ivb-3520m     total:288  pass:259  dwarn:0   dfail:0   fail:0   skip:29  time:459s
fi-ivb-3770      total:288  pass:255  dwarn:0   dfail:0   fail:0   skip:33  time:421s
fi-kbl-7500u     total:288  pass:263  dwarn:1   dfail:0   fail:0   skip:24  time:468s
fi-kbl-7567u     total:288  pass:268  dwarn:0   dfail:0   fail:0   skip:20  time:468s
fi-kbl-r         total:288  pass:261  dwarn:0   dfail:0   fail:0   skip:27  time:510s
fi-pnv-d510      total:288  pass:222  dwarn:1   dfail:0   fail:0   skip:65  time:595s
fi-skl-6260u     total:288  pass:268  dwarn:0   dfail:0   fail:0   skip:20  time:434s
fi-skl-6600u     total:288  pass:261  dwarn:0   dfail:0   fail:0   skip:27  time:523s
fi-skl-6700hq    total:288  pass:262  dwarn:0   dfail:0   fail:0   skip:26  time:535s
fi-skl-6700k2    total:288  pass:264  dwarn:0   dfail:0   fail:0   skip:24  time:503s
fi-skl-6770hq    total:288  pass:268  dwarn:0   dfail:0   fail:0   skip:20  time:490s
fi-skl-guc       total:288  pass:260  dwarn:0   dfail:0   fail:0   skip:28  time:421s
fi-skl-gvtdvm    total:288  pass:265  dwarn:0   dfail:0   fail:0   skip:23  time:429s
fi-snb-2520m     total:245  pass:211  dwarn:0   dfail:0   fail:0   skip:33 
fi-snb-2600      total:288  pass:248  dwarn:0   dfail:0   fail:0   skip:40  time:399s

== Logs ==

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

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

* Re: [PATCH igt] igt: Add gem_ctx_freq to exercise requesting freq on a ctx
  2018-03-07 22:49 ` [igt-dev] " Chris Wilson
@ 2018-03-08  0:55   ` Antonio Argenziano
  -1 siblings, 0 replies; 36+ messages in thread
From: Antonio Argenziano @ 2018-03-08  0:55 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx; +Cc: igt-dev



On 07/03/18 14:49, Chris Wilson wrote:
> Exercise some new API that allows applications to request that
> individual contexts are executed within a desired frequency range.
> 
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> ---
>   tests/Makefile.am      |   2 +-
>   tests/Makefile.sources |   1 +
>   tests/gem_ctx_freq.c   | 190 +++++++++++++++++++++++++++++++++++++++++++++++++
>   tests/meson.build      |   1 +
>   4 files changed, 193 insertions(+), 1 deletion(-)
>   create mode 100644 tests/gem_ctx_freq.c
> 
> diff --git a/tests/Makefile.am b/tests/Makefile.am
> index edd689a4..f42641f6 100644
> --- a/tests/Makefile.am
> +++ b/tests/Makefile.am
> @@ -104,6 +104,7 @@ drm_import_export_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
>   drm_import_export_LDADD = $(LDADD) -lpthread
>   gem_close_race_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
>   gem_close_race_LDADD = $(LDADD) -lpthread
> +gem_ctx_freq_LDADD = $(LDADD) $(top_builddir)/lib/libigt_perf.la
>   gem_ctx_thrash_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
>   gem_ctx_thrash_LDADD = $(LDADD) -lpthread
>   gem_exec_parallel_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
> @@ -128,7 +129,6 @@ prime_self_import_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
>   prime_self_import_LDADD = $(LDADD) -lpthread
>   gem_userptr_blits_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
>   gem_userptr_blits_LDADD = $(LDADD) -lpthread
> -perf_pmu_LDADD = $(LDADD) $(top_builddir)/lib/libigt_perf.la
>   
>   gem_wait_LDADD = $(LDADD) -lrt
>   kms_flip_LDADD = $(LDADD) -lrt -lpthread
> diff --git a/tests/Makefile.sources b/tests/Makefile.sources
> index 05cdc1ef..06e729ef 100644
> --- a/tests/Makefile.sources
> +++ b/tests/Makefile.sources
> @@ -59,6 +59,7 @@ TESTS_progs = \
>   	gem_ctx_bad_exec \
>   	gem_ctx_create \
>   	gem_ctx_exec \
> +	gem_ctx_freq \
>   	gem_ctx_isolation \
>   	gem_ctx_param \
>   	gem_ctx_shared \
> diff --git a/tests/gem_ctx_freq.c b/tests/gem_ctx_freq.c
> new file mode 100644
> index 00000000..a01ce01b
> --- /dev/null
> +++ b/tests/gem_ctx_freq.c
> @@ -0,0 +1,190 @@
> +/*
> + * 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 <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 <time.h>
> +
> +#include "igt.h"
> +#include "igt_perf.h"
> +
> +#define LOCAL_CONTEXT_PARAM_FREQUENCY 8
> +
> +static void set_freq(int fd, uint32_t ctx, uint32_t min, uint32_t max)
> +{
> +	struct drm_i915_gem_context_param param = {
> +		.ctx_id = ctx,
> +		.param = LOCAL_CONTEXT_PARAM_FREQUENCY,
> +		.value = (uint64_t)max << 32 | min,
> +	};
> +
> +	gem_context_set_param(fd, &param);
> +}
> +
> +static void get_freq(int fd, uint32_t ctx, uint32_t *min, uint32_t *max)
> +{
> +	struct drm_i915_gem_context_param param = {
> +		.ctx_id = ctx,
> +		.param = LOCAL_CONTEXT_PARAM_FREQUENCY,
> +	};
> +
> +	gem_context_get_param(fd, &param);
> +
> +	*min = param.value & 0xffffffff;
> +	*max = param.value >> 32;
> +}
> +
> +static double measure_frequency(int pmu, int delay)
> +{
> +	uint64_t data[2];
> +	uint64_t d_t, d_v;
> +
> +	igt_assert_eq(read(pmu, data, sizeof(data)), sizeof(data));
> +	d_v = -data[0];
> +	d_t = -data[1];
> +
> +	usleep(delay);
> +
> +	igt_assert_eq(read(pmu, data, sizeof(data)), sizeof(data));
> +	d_v += data[0];
> +	d_t += data[1];
> +
> +	return d_v * 1e9 / d_t;
> +}
> +
> +static void single(int fd, const struct intel_execution_engine *e)
> +{
> +	const unsigned int engine = e->exec_id | e->flags;
> +	uint32_t ctx = gem_context_create(fd);
> +	uint32_t min, max;
> +	double measured;
> +	igt_spin_t *spin;
> +	int pmu;
> +
> +	get_freq(fd, ctx, &min, &max);
> +	igt_info("Min freq: %dMHz; Max freq: %dMHz\n", min, max);
> +
> +	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
> +	igt_require(pmu >= 0);
> +
> +	gem_quiescent_gpu(fd);
> +	measured = measure_frequency(pmu, 10000);
> +	igt_info("Initial (idle) freq: %.1fMHz\n",measured);
> +	igt_require(measured >= min - 50 && measured <= min + 50);
> +
> +	for (uint32_t freq = min + 50; freq <= max; freq += 100) {
> +		set_freq(fd, ctx, freq, freq);
> +
> +		gem_quiescent_gpu(fd);
> +		spin = __igt_spin_batch_new(fd, ctx, engine, 0);
> +		usleep(10000);
> +
> +		measured = measure_frequency(pmu, 50000);
> +		igt_debugfs_dump(fd, "i915_rps_boost_info");
> +
> +		igt_spin_batch_free(fd, spin);
> +		igt_info("%s(single): Measured %.1fMHz, expected %dMhz\n",
> +			 e->name, measured, freq);
> +		igt_assert(measured > freq - 100 && measured < freq + 100);
> +	}
> +	gem_quiescent_gpu(fd);

Check frequency has gone back to ~min.

> +

I would suggest to split here into two sub-tests.

> +	spin = __igt_spin_batch_new(fd, ctx, engine, 0);
> +	for (uint32_t freq = min + 50; freq <= max; freq += 100) {
> +		igt_spin_t *kick;
> +
> +		set_freq(fd, ctx, freq, freq);
> +
> +		/*
> +		 * When requesting a new frequency on the currently
> +		 * executing context, it does not take effect until the
> +		 * next context switch. In this case, we trigger a lite
> +		 * restore.

Is this enforced by the ABI? If so, we should check that after 
set_freq() nothing changed.

> +		 */
> +		kick = __igt_spin_batch_new(fd, ctx, engine, 0);
> +		igt_spin_batch_free(fd, spin);
> +		spin = kick;
> +
> +		usleep(10000);
> +
> +		measured = measure_frequency(pmu, 50000);
> +		igt_debugfs_dump(fd, "i915_rps_boost_info");
> +
> +		igt_info("%s(continuous): Measured %.1fMHz, expected %dMhz\n",
> +			 e->name, measured, freq);
> +		igt_assert(measured > freq - 100 && measured < freq + 100);
> +	}
> +	igt_spin_batch_free(fd, spin);
> +
> +	gem_quiescent_gpu(fd);
> +	measured = measure_frequency(pmu, 10000);
> +	igt_info("Final (idle) freq: %.1fMHz\n", measured);
> +	igt_assert(measured >= min - 50 && measured <= min + 50);
> +
> +	close(pmu);
> +	gem_context_destroy(fd, ctx);
> +}
> +
> +static bool has_ctx_freq(int fd)
> +{
> +	struct drm_i915_gem_context_param param = {
> +		.param = LOCAL_CONTEXT_PARAM_FREQUENCY,
> +	};
> +
> +	return __gem_context_get_param(fd, &param) == 0;
> +}
> +
> +igt_main
> +{
> +	const struct intel_execution_engine *e;
> +	int fd = -1;
> +
> +	igt_fixture {
> +		fd = drm_open_driver(DRIVER_INTEL);
> +		igt_require_gem(fd);
> +
> +		igt_require(has_ctx_freq(fd));
> +	}
> +
> +	igt_skip_on_simulation();
> +
> +	for (e = intel_execution_engines; e->name; e++) {
> +		if (e->exec_id == 0)
> +			continue;
> +
> +		igt_subtest_f("%s%s", e->exec_id == 0 ? "basic-" : "", e->name) {

I believe this is a typo, exec_id would never be 0 at this check. Maybe 
it should have been ("basic-%s", e->name).

Thanks,
Antonio

> +			igt_require(gem_ring_has_physical_engine(fd, e->exec_id | e->flags));
> +			single(fd, e);
> +		}
> +	}
> +}
> diff --git a/tests/meson.build b/tests/meson.build
> index 58729231..f1271274 100644
> --- a/tests/meson.build
> +++ b/tests/meson.build
> @@ -35,6 +35,7 @@ test_progs = [
>   	'gem_ctx_bad_exec',
>   	'gem_ctx_create',
>   	'gem_ctx_exec',
> +	'gem_ctx_freq',
>   	'gem_ctx_param',
>   	'gem_ctx_switch',
>   	'gem_ctx_thrash',
> 
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* Re: [Intel-gfx] [PATCH igt] igt: Add gem_ctx_freq to exercise requesting freq on a ctx
@ 2018-03-08  0:55   ` Antonio Argenziano
  0 siblings, 0 replies; 36+ messages in thread
From: Antonio Argenziano @ 2018-03-08  0:55 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx; +Cc: igt-dev



On 07/03/18 14:49, Chris Wilson wrote:
> Exercise some new API that allows applications to request that
> individual contexts are executed within a desired frequency range.
> 
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> ---
>   tests/Makefile.am      |   2 +-
>   tests/Makefile.sources |   1 +
>   tests/gem_ctx_freq.c   | 190 +++++++++++++++++++++++++++++++++++++++++++++++++
>   tests/meson.build      |   1 +
>   4 files changed, 193 insertions(+), 1 deletion(-)
>   create mode 100644 tests/gem_ctx_freq.c
> 
> diff --git a/tests/Makefile.am b/tests/Makefile.am
> index edd689a4..f42641f6 100644
> --- a/tests/Makefile.am
> +++ b/tests/Makefile.am
> @@ -104,6 +104,7 @@ drm_import_export_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
>   drm_import_export_LDADD = $(LDADD) -lpthread
>   gem_close_race_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
>   gem_close_race_LDADD = $(LDADD) -lpthread
> +gem_ctx_freq_LDADD = $(LDADD) $(top_builddir)/lib/libigt_perf.la
>   gem_ctx_thrash_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
>   gem_ctx_thrash_LDADD = $(LDADD) -lpthread
>   gem_exec_parallel_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
> @@ -128,7 +129,6 @@ prime_self_import_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
>   prime_self_import_LDADD = $(LDADD) -lpthread
>   gem_userptr_blits_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
>   gem_userptr_blits_LDADD = $(LDADD) -lpthread
> -perf_pmu_LDADD = $(LDADD) $(top_builddir)/lib/libigt_perf.la
>   
>   gem_wait_LDADD = $(LDADD) -lrt
>   kms_flip_LDADD = $(LDADD) -lrt -lpthread
> diff --git a/tests/Makefile.sources b/tests/Makefile.sources
> index 05cdc1ef..06e729ef 100644
> --- a/tests/Makefile.sources
> +++ b/tests/Makefile.sources
> @@ -59,6 +59,7 @@ TESTS_progs = \
>   	gem_ctx_bad_exec \
>   	gem_ctx_create \
>   	gem_ctx_exec \
> +	gem_ctx_freq \
>   	gem_ctx_isolation \
>   	gem_ctx_param \
>   	gem_ctx_shared \
> diff --git a/tests/gem_ctx_freq.c b/tests/gem_ctx_freq.c
> new file mode 100644
> index 00000000..a01ce01b
> --- /dev/null
> +++ b/tests/gem_ctx_freq.c
> @@ -0,0 +1,190 @@
> +/*
> + * 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 <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 <time.h>
> +
> +#include "igt.h"
> +#include "igt_perf.h"
> +
> +#define LOCAL_CONTEXT_PARAM_FREQUENCY 8
> +
> +static void set_freq(int fd, uint32_t ctx, uint32_t min, uint32_t max)
> +{
> +	struct drm_i915_gem_context_param param = {
> +		.ctx_id = ctx,
> +		.param = LOCAL_CONTEXT_PARAM_FREQUENCY,
> +		.value = (uint64_t)max << 32 | min,
> +	};
> +
> +	gem_context_set_param(fd, &param);
> +}
> +
> +static void get_freq(int fd, uint32_t ctx, uint32_t *min, uint32_t *max)
> +{
> +	struct drm_i915_gem_context_param param = {
> +		.ctx_id = ctx,
> +		.param = LOCAL_CONTEXT_PARAM_FREQUENCY,
> +	};
> +
> +	gem_context_get_param(fd, &param);
> +
> +	*min = param.value & 0xffffffff;
> +	*max = param.value >> 32;
> +}
> +
> +static double measure_frequency(int pmu, int delay)
> +{
> +	uint64_t data[2];
> +	uint64_t d_t, d_v;
> +
> +	igt_assert_eq(read(pmu, data, sizeof(data)), sizeof(data));
> +	d_v = -data[0];
> +	d_t = -data[1];
> +
> +	usleep(delay);
> +
> +	igt_assert_eq(read(pmu, data, sizeof(data)), sizeof(data));
> +	d_v += data[0];
> +	d_t += data[1];
> +
> +	return d_v * 1e9 / d_t;
> +}
> +
> +static void single(int fd, const struct intel_execution_engine *e)
> +{
> +	const unsigned int engine = e->exec_id | e->flags;
> +	uint32_t ctx = gem_context_create(fd);
> +	uint32_t min, max;
> +	double measured;
> +	igt_spin_t *spin;
> +	int pmu;
> +
> +	get_freq(fd, ctx, &min, &max);
> +	igt_info("Min freq: %dMHz; Max freq: %dMHz\n", min, max);
> +
> +	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
> +	igt_require(pmu >= 0);
> +
> +	gem_quiescent_gpu(fd);
> +	measured = measure_frequency(pmu, 10000);
> +	igt_info("Initial (idle) freq: %.1fMHz\n",measured);
> +	igt_require(measured >= min - 50 && measured <= min + 50);
> +
> +	for (uint32_t freq = min + 50; freq <= max; freq += 100) {
> +		set_freq(fd, ctx, freq, freq);
> +
> +		gem_quiescent_gpu(fd);
> +		spin = __igt_spin_batch_new(fd, ctx, engine, 0);
> +		usleep(10000);
> +
> +		measured = measure_frequency(pmu, 50000);
> +		igt_debugfs_dump(fd, "i915_rps_boost_info");
> +
> +		igt_spin_batch_free(fd, spin);
> +		igt_info("%s(single): Measured %.1fMHz, expected %dMhz\n",
> +			 e->name, measured, freq);
> +		igt_assert(measured > freq - 100 && measured < freq + 100);
> +	}
> +	gem_quiescent_gpu(fd);

Check frequency has gone back to ~min.

> +

I would suggest to split here into two sub-tests.

> +	spin = __igt_spin_batch_new(fd, ctx, engine, 0);
> +	for (uint32_t freq = min + 50; freq <= max; freq += 100) {
> +		igt_spin_t *kick;
> +
> +		set_freq(fd, ctx, freq, freq);
> +
> +		/*
> +		 * When requesting a new frequency on the currently
> +		 * executing context, it does not take effect until the
> +		 * next context switch. In this case, we trigger a lite
> +		 * restore.

Is this enforced by the ABI? If so, we should check that after 
set_freq() nothing changed.

> +		 */
> +		kick = __igt_spin_batch_new(fd, ctx, engine, 0);
> +		igt_spin_batch_free(fd, spin);
> +		spin = kick;
> +
> +		usleep(10000);
> +
> +		measured = measure_frequency(pmu, 50000);
> +		igt_debugfs_dump(fd, "i915_rps_boost_info");
> +
> +		igt_info("%s(continuous): Measured %.1fMHz, expected %dMhz\n",
> +			 e->name, measured, freq);
> +		igt_assert(measured > freq - 100 && measured < freq + 100);
> +	}
> +	igt_spin_batch_free(fd, spin);
> +
> +	gem_quiescent_gpu(fd);
> +	measured = measure_frequency(pmu, 10000);
> +	igt_info("Final (idle) freq: %.1fMHz\n", measured);
> +	igt_assert(measured >= min - 50 && measured <= min + 50);
> +
> +	close(pmu);
> +	gem_context_destroy(fd, ctx);
> +}
> +
> +static bool has_ctx_freq(int fd)
> +{
> +	struct drm_i915_gem_context_param param = {
> +		.param = LOCAL_CONTEXT_PARAM_FREQUENCY,
> +	};
> +
> +	return __gem_context_get_param(fd, &param) == 0;
> +}
> +
> +igt_main
> +{
> +	const struct intel_execution_engine *e;
> +	int fd = -1;
> +
> +	igt_fixture {
> +		fd = drm_open_driver(DRIVER_INTEL);
> +		igt_require_gem(fd);
> +
> +		igt_require(has_ctx_freq(fd));
> +	}
> +
> +	igt_skip_on_simulation();
> +
> +	for (e = intel_execution_engines; e->name; e++) {
> +		if (e->exec_id == 0)
> +			continue;
> +
> +		igt_subtest_f("%s%s", e->exec_id == 0 ? "basic-" : "", e->name) {

I believe this is a typo, exec_id would never be 0 at this check. Maybe 
it should have been ("basic-%s", e->name).

Thanks,
Antonio

> +			igt_require(gem_ring_has_physical_engine(fd, e->exec_id | e->flags));
> +			single(fd, e);
> +		}
> +	}
> +}
> diff --git a/tests/meson.build b/tests/meson.build
> index 58729231..f1271274 100644
> --- a/tests/meson.build
> +++ b/tests/meson.build
> @@ -35,6 +35,7 @@ test_progs = [
>   	'gem_ctx_bad_exec',
>   	'gem_ctx_create',
>   	'gem_ctx_exec',
> +	'gem_ctx_freq',
>   	'gem_ctx_param',
>   	'gem_ctx_switch',
>   	'gem_ctx_thrash',
> 
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* Re: [PATCH igt] igt: Add gem_ctx_freq to exercise requesting freq on a ctx
  2018-03-08  0:55   ` [Intel-gfx] " Antonio Argenziano
@ 2018-03-08  1:18     ` Chris Wilson
  -1 siblings, 0 replies; 36+ messages in thread
From: Chris Wilson @ 2018-03-08  1:18 UTC (permalink / raw)
  To: Antonio Argenziano, intel-gfx; +Cc: igt-dev

Quoting Antonio Argenziano (2018-03-08 00:55:47)
> 
> 
> On 07/03/18 14:49, Chris Wilson wrote:
> > +static void single(int fd, const struct intel_execution_engine *e)
> > +{
> > +     const unsigned int engine = e->exec_id | e->flags;
> > +     uint32_t ctx = gem_context_create(fd);
> > +     uint32_t min, max;
> > +     double measured;
> > +     igt_spin_t *spin;
> > +     int pmu;
> > +
> > +     get_freq(fd, ctx, &min, &max);
> > +     igt_info("Min freq: %dMHz; Max freq: %dMHz\n", min, max);
> > +
> > +     pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
> > +     igt_require(pmu >= 0);
> > +
> > +     gem_quiescent_gpu(fd);
> > +     measured = measure_frequency(pmu, 10000);
> > +     igt_info("Initial (idle) freq: %.1fMHz\n",measured);
> > +     igt_require(measured >= min - 50 && measured <= min + 50);
> > +
> > +     for (uint32_t freq = min + 50; freq <= max; freq += 100) {
> > +             set_freq(fd, ctx, freq, freq);
> > +
> > +             gem_quiescent_gpu(fd);
> > +             spin = __igt_spin_batch_new(fd, ctx, engine, 0);
> > +             usleep(10000);
> > +
> > +             measured = measure_frequency(pmu, 50000);
> > +             igt_debugfs_dump(fd, "i915_rps_boost_info");
> > +
> > +             igt_spin_batch_free(fd, spin);
> > +             igt_info("%s(single): Measured %.1fMHz, expected %dMhz\n",
> > +                      e->name, measured, freq);
> > +             igt_assert(measured > freq - 100 && measured < freq + 100);
> > +     }
> > +     gem_quiescent_gpu(fd);
> 
> Check frequency has gone back to ~min.

It's not that interesting a test (covered already by pmu) as we
essentially lie anyway over idle.

> I would suggest to split here into two sub-tests.
> 
> > +     spin = __igt_spin_batch_new(fd, ctx, engine, 0);
> > +     for (uint32_t freq = min + 50; freq <= max; freq += 100) {
> > +             igt_spin_t *kick;
> > +
> > +             set_freq(fd, ctx, freq, freq);
> > +
> > +             /*
> > +              * When requesting a new frequency on the currently
> > +              * executing context, it does not take effect until the
> > +              * next context switch. In this case, we trigger a lite
> > +              * restore.
> 
> Is this enforced by the ABI? 

Enforced? No. The comment is precisely because it's not checked on
calling whether the context is currently on the HW and trying hard to be
sure that no one expects us to do that check. i.e. that set_freq()
doesn't change frequency itself, but doesn't rule it out either as it
may appear to have that effect due to many external factors.
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* Re: [igt-dev] [Intel-gfx] [PATCH igt] igt: Add gem_ctx_freq to exercise requesting freq on a ctx
@ 2018-03-08  1:18     ` Chris Wilson
  0 siblings, 0 replies; 36+ messages in thread
From: Chris Wilson @ 2018-03-08  1:18 UTC (permalink / raw)
  To: Antonio Argenziano, intel-gfx; +Cc: igt-dev

Quoting Antonio Argenziano (2018-03-08 00:55:47)
> 
> 
> On 07/03/18 14:49, Chris Wilson wrote:
> > +static void single(int fd, const struct intel_execution_engine *e)
> > +{
> > +     const unsigned int engine = e->exec_id | e->flags;
> > +     uint32_t ctx = gem_context_create(fd);
> > +     uint32_t min, max;
> > +     double measured;
> > +     igt_spin_t *spin;
> > +     int pmu;
> > +
> > +     get_freq(fd, ctx, &min, &max);
> > +     igt_info("Min freq: %dMHz; Max freq: %dMHz\n", min, max);
> > +
> > +     pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
> > +     igt_require(pmu >= 0);
> > +
> > +     gem_quiescent_gpu(fd);
> > +     measured = measure_frequency(pmu, 10000);
> > +     igt_info("Initial (idle) freq: %.1fMHz\n",measured);
> > +     igt_require(measured >= min - 50 && measured <= min + 50);
> > +
> > +     for (uint32_t freq = min + 50; freq <= max; freq += 100) {
> > +             set_freq(fd, ctx, freq, freq);
> > +
> > +             gem_quiescent_gpu(fd);
> > +             spin = __igt_spin_batch_new(fd, ctx, engine, 0);
> > +             usleep(10000);
> > +
> > +             measured = measure_frequency(pmu, 50000);
> > +             igt_debugfs_dump(fd, "i915_rps_boost_info");
> > +
> > +             igt_spin_batch_free(fd, spin);
> > +             igt_info("%s(single): Measured %.1fMHz, expected %dMhz\n",
> > +                      e->name, measured, freq);
> > +             igt_assert(measured > freq - 100 && measured < freq + 100);
> > +     }
> > +     gem_quiescent_gpu(fd);
> 
> Check frequency has gone back to ~min.

It's not that interesting a test (covered already by pmu) as we
essentially lie anyway over idle.

> I would suggest to split here into two sub-tests.
> 
> > +     spin = __igt_spin_batch_new(fd, ctx, engine, 0);
> > +     for (uint32_t freq = min + 50; freq <= max; freq += 100) {
> > +             igt_spin_t *kick;
> > +
> > +             set_freq(fd, ctx, freq, freq);
> > +
> > +             /*
> > +              * When requesting a new frequency on the currently
> > +              * executing context, it does not take effect until the
> > +              * next context switch. In this case, we trigger a lite
> > +              * restore.
> 
> Is this enforced by the ABI? 

Enforced? No. The comment is precisely because it's not checked on
calling whether the context is currently on the HW and trying hard to be
sure that no one expects us to do that check. i.e. that set_freq()
doesn't change frequency itself, but doesn't rule it out either as it
may appear to have that effect due to many external factors.
-Chris
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

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

* [igt-dev] ✗ Fi.CI.IGT: failure for igt: Add gem_ctx_freq to exercise requesting freq on a ctx (rev2)
  2018-03-07 22:49 ` [igt-dev] " Chris Wilson
                   ` (4 preceding siblings ...)
  (?)
@ 2018-03-08  1:49 ` Patchwork
  -1 siblings, 0 replies; 36+ messages in thread
From: Patchwork @ 2018-03-08  1:49 UTC (permalink / raw)
  To: Chris Wilson; +Cc: igt-dev

== Series Details ==

Series: igt: Add gem_ctx_freq to exercise requesting freq on a ctx (rev2)
URL   : https://patchwork.freedesktop.org/series/39564/
State : failure

== Summary ==

---- Possible new issues:

Test gem_exec_capture:
        Subgroup capture-vebox:
                pass       -> FAIL       (shard-apl)

---- Known issues:

Test gem_softpin:
        Subgroup noreloc-s3:
                skip       -> PASS       (shard-snb) fdo#103375
Test kms_chv_cursor_fail:
        Subgroup pipe-b-128x128-bottom-edge:
                pass       -> DMESG-WARN (shard-snb) fdo#105185 +2
Test kms_flip:
        Subgroup 2x-flip-vs-expired-vblank:
                pass       -> FAIL       (shard-hsw) fdo#102887 +1
        Subgroup 2x-plain-flip-ts-check:
                fail       -> PASS       (shard-hsw) fdo#100368 +1
Test kms_frontbuffer_tracking:
        Subgroup fbc-suspend:
                fail       -> PASS       (shard-apl) fdo#101623
Test kms_rotation_crc:
        Subgroup primary-rotation-180:
                fail       -> PASS       (shard-snb) fdo#103925

fdo#103375 https://bugs.freedesktop.org/show_bug.cgi?id=103375
fdo#105185 https://bugs.freedesktop.org/show_bug.cgi?id=105185
fdo#102887 https://bugs.freedesktop.org/show_bug.cgi?id=102887
fdo#100368 https://bugs.freedesktop.org/show_bug.cgi?id=100368
fdo#101623 https://bugs.freedesktop.org/show_bug.cgi?id=101623
fdo#103925 https://bugs.freedesktop.org/show_bug.cgi?id=103925

shard-apl        total:3471 pass:1820 dwarn:1   dfail:0   fail:8   skip:1640 time:12015s
shard-hsw        total:3477 pass:1770 dwarn:1   dfail:0   fail:4   skip:1701 time:11919s
shard-snb        total:3477 pass:1364 dwarn:2   dfail:0   fail:1   skip:2110 time:7170s
Blacklisted hosts:
shard-kbl        total:3471 pass:1941 dwarn:7   dfail:0   fail:8   skip:1514 time:9363s

== Logs ==

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

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

* [PATCH igt] igt: Add gem_ctx_freq to exercise requesting freq on a ctx
  2018-03-07 22:49 ` [igt-dev] " Chris Wilson
                   ` (5 preceding siblings ...)
  (?)
@ 2018-03-08  1:59 ` Chris Wilson
  -1 siblings, 0 replies; 36+ messages in thread
From: Chris Wilson @ 2018-03-08  1:59 UTC (permalink / raw)
  To: intel-gfx

Exercise some new API that allows applications to request that
individual contexts are executed within a desired frequency range.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
A few more test ideas.
---
 tests/Makefile.am      |   1 +
 tests/Makefile.sources |   1 +
 tests/gem_ctx_freq.c   | 512 +++++++++++++++++++++++++++++++++++++++++++++++++
 tests/meson.build      |   1 +
 4 files changed, 515 insertions(+)
 create mode 100644 tests/gem_ctx_freq.c

diff --git a/tests/Makefile.am b/tests/Makefile.am
index dbc7be72..389f7fc7 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -104,6 +104,7 @@ drm_import_export_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 drm_import_export_LDADD = $(LDADD) -lpthread
 gem_close_race_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_close_race_LDADD = $(LDADD) -lpthread
+gem_ctx_freq_LDADD = $(LDADD) $(top_builddir)/lib/libigt_perf.la
 gem_ctx_thrash_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_ctx_thrash_LDADD = $(LDADD) -lpthread
 gem_exec_parallel_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index 4a81ac4a..3d079c42 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -58,6 +58,7 @@ TESTS_progs = \
 	gem_ctx_bad_exec \
 	gem_ctx_create \
 	gem_ctx_exec \
+	gem_ctx_freq \
 	gem_ctx_isolation \
 	gem_ctx_param \
 	gem_ctx_switch \
diff --git a/tests/gem_ctx_freq.c b/tests/gem_ctx_freq.c
new file mode 100644
index 00000000..bd60837d
--- /dev/null
+++ b/tests/gem_ctx_freq.c
@@ -0,0 +1,512 @@
+/*
+ * 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 <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 <time.h>
+
+#include "igt.h"
+#include "igt_perf.h"
+
+#define LOCAL_CONTEXT_PARAM_FREQUENCY 8
+
+static int __set_freq(int fd, uint32_t ctx, uint32_t min, uint32_t max)
+{
+	struct drm_i915_gem_context_param param = {
+		.ctx_id = ctx,
+		.param = LOCAL_CONTEXT_PARAM_FREQUENCY,
+		.value = (uint64_t)max << 32 | min,
+	};
+
+	return __gem_context_set_param(fd, &param);
+}
+
+static void set_freq(int fd, uint32_t ctx, uint32_t min, uint32_t max)
+{
+	igt_assert_eq(__set_freq(fd, ctx, min, max), 0);
+}
+
+static void get_freq(int fd, uint32_t ctx, uint32_t *min, uint32_t *max)
+{
+	struct drm_i915_gem_context_param param = {
+		.ctx_id = ctx,
+		.param = LOCAL_CONTEXT_PARAM_FREQUENCY,
+	};
+
+	gem_context_get_param(fd, &param);
+
+	*min = param.value & 0xffffffff;
+	*max = param.value >> 32;
+}
+
+static double measure_frequency(int pmu, int delay)
+{
+	uint64_t data[2];
+	uint64_t d_t, d_v;
+
+	igt_assert_eq(read(pmu, data, sizeof(data)), sizeof(data));
+	d_v = -data[0];
+	d_t = -data[1];
+
+	usleep(delay);
+
+	igt_assert_eq(read(pmu, data, sizeof(data)), sizeof(data));
+	d_v += data[0];
+	d_t += data[1];
+
+	return d_v * 1e9 / d_t;
+}
+
+static void single(int fd, const struct intel_execution_engine *e)
+{
+	const unsigned int engine = e->exec_id | e->flags;
+	uint32_t ctx = gem_context_create(fd);
+	uint32_t min, max;
+	double measured;
+	igt_spin_t *spin;
+	int pmu;
+
+	get_freq(fd, ctx, &min, &max);
+	igt_info("Min freq: %dMHz; Max freq: %dMHz\n", min, max);
+
+	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
+	igt_require(pmu >= 0);
+
+	gem_quiescent_gpu(fd);
+	measured = measure_frequency(pmu, 10000);
+	igt_info("Initial (idle) freq: %.1fMHz\n",measured);
+	igt_require(measured >= min - 50 && measured <= min + 50);
+
+	for (uint32_t freq = min + 50; freq <= max; freq += 100) {
+		uint32_t cur, discard;
+
+		set_freq(fd, ctx, freq, freq);
+		get_freq(fd, ctx, &cur, &discard);
+
+		gem_quiescent_gpu(fd);
+		spin = __igt_spin_batch_new(fd, ctx, engine, 0);
+		usleep(10000);
+
+		measured = measure_frequency(pmu, 50000);
+		igt_debugfs_dump(fd, "i915_rps_boost_info");
+
+		igt_spin_batch_free(fd, spin);
+		igt_info("%s(single): Measured %.1fMHz, expected %dMhz\n",
+			 e->name, measured, cur);
+		igt_assert(measured > cur - 100 && measured < cur + 100);
+	}
+	gem_quiescent_gpu(fd);
+
+	spin = __igt_spin_batch_new(fd, ctx, engine, 0);
+	for (uint32_t freq = min + 50; freq <= max; freq += 100) {
+		uint32_t cur, discard;
+		igt_spin_t *kick;
+
+		set_freq(fd, ctx, freq, freq);
+		get_freq(fd, ctx, &cur, &discard);
+
+		/*
+		 * When requesting a new frequency on the currently
+		 * executing context, it does not take effect until the
+		 * next context switch. In this case, we trigger a lite
+		 * restore.
+		 */
+		kick = __igt_spin_batch_new(fd, ctx, engine, 0);
+		igt_spin_batch_free(fd, spin);
+		spin = kick;
+
+		usleep(10000);
+
+		measured = measure_frequency(pmu, 50000);
+		igt_debugfs_dump(fd, "i915_rps_boost_info");
+
+		igt_info("%s(continuous): Measured %.1fMHz, expected %dMhz\n",
+			 e->name, measured, cur);
+		igt_assert(measured > cur - 100 && measured < cur + 100);
+	}
+	igt_spin_batch_free(fd, spin);
+
+	gem_quiescent_gpu(fd);
+	measured = measure_frequency(pmu, 10000);
+	igt_info("Final (idle) freq: %.1fMHz\n", measured);
+	igt_assert(measured >= min - 50 && measured <= min + 50);
+
+	close(pmu);
+	gem_context_destroy(fd, ctx);
+}
+
+static void inflight(int fd, const struct intel_execution_engine *e)
+{
+	const unsigned int engine = e->exec_id | e->flags;
+	uint32_t ctx, min, max, freq, discard;
+	double measured;
+	igt_spin_t *plug, *spin;
+	int pmu;
+
+	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
+	igt_require(pmu >= 0);
+
+	ctx = gem_context_create(fd);
+	get_freq(fd, ctx, &min, &max);
+	set_freq(fd, ctx, min, min);
+
+	igt_info("Min freq: %dMHz; Max freq: %dMHz\n", min, max);
+
+	plug = igt_spin_batch_new(fd, ctx, engine, 0);
+	gem_context_destroy(fd, ctx);
+	for (int n = 0; n < 16; n++) {
+		struct drm_i915_gem_exec_object2 obj = {
+			.handle = plug->handle,
+		};
+		struct drm_i915_gem_execbuffer2 eb = {
+			.buffer_count = 1,
+			.buffers_ptr = to_user_pointer(&obj),
+			.flags = engine,
+			.rsvd1 = gem_context_create(fd),
+		};
+		set_freq(fd, eb.rsvd1, min, min);
+		gem_execbuf(fd, &eb);
+		gem_context_destroy(fd, eb.rsvd1);
+	}
+	measured = measure_frequency(pmu, 50000);
+	igt_debugfs_dump(fd, "i915_rps_boost_info");
+	igt_info("%s(plug): Measured %.1fMHz, expected %dMhz\n",
+		 e->name, measured, min);
+	igt_assert(measured > min - 100 && measured < min + 100);
+
+	ctx = gem_context_create(fd);
+	set_freq(fd, ctx, max, max);
+	spin = __igt_spin_batch_new(fd, ctx, engine, 0);
+
+	/* spin is now queued but not executing */
+	freq = (max + min) / 2;
+	set_freq(fd, ctx, freq, freq);
+	get_freq(fd, ctx, &freq, &discard);
+	gem_context_destroy(fd, ctx);
+	igt_spin_batch_end(plug);
+
+	do
+		usleep(10000);
+	while (gem_bo_busy(fd, plug->handle));
+	igt_spin_batch_free(fd, plug);
+
+	/* Now spin will execute */
+	measured = measure_frequency(pmu, 50000);
+	igt_debugfs_dump(fd, "i915_engine_info");
+	igt_debugfs_dump(fd, "i915_rps_boost_info");
+	igt_info("%s(work): Measured %.1fMHz, expected %dMhz\n",
+		 e->name, measured, freq);
+	igt_assert(measured > freq - 100 && measured < freq + 100);
+
+	igt_spin_batch_free(fd, spin);
+	close(pmu);
+	gem_quiescent_gpu(fd);
+}
+
+static void sandwich(int fd)
+{
+	uint32_t ctx = gem_context_create(fd);
+	unsigned int engine;
+	uint32_t min, max;
+	igt_spin_t *spin;
+	int pmu;
+
+	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
+	igt_require(pmu >= 0);
+
+	spin = igt_spin_batch_new(fd, ctx, 0, 0);
+	get_freq(fd, ctx, &min, &max);
+	set_freq(fd, ctx, min, min);
+	for_each_physical_engine(fd, engine) {
+		struct drm_i915_gem_exec_object2 obj = {
+			.handle = spin->handle,
+		};
+		struct drm_i915_gem_execbuffer2 eb = {
+			.buffer_count = 1,
+			.buffers_ptr = to_user_pointer(&obj),
+			.flags = engine,
+			.rsvd1 = ctx,
+		};
+		uint32_t cur, discard;
+		double measured;
+
+		min += 50;
+		if (min > max)
+			break;
+
+		set_freq(fd, ctx, min, min);
+		get_freq(fd, ctx, &cur, &discard);
+
+		gem_execbuf(fd, &eb);
+		usleep(10000);
+
+		measured = measure_frequency(pmu, 50000);
+		igt_debugfs_dump(fd, "i915_rps_boost_info");
+
+		igt_info("Measured %.1fMHz, expected %dMhz\n", measured, cur);
+		igt_assert(measured > cur - 100 && measured < cur + 100);
+	}
+	igt_spin_batch_free(fd, spin);
+	gem_quiescent_gpu(fd);
+
+	gem_context_destroy(fd, ctx);
+	close(pmu);
+}
+
+static void smoketest(int fd, int timeout)
+{
+	unsigned int engines[16];
+	unsigned int nengine;
+	unsigned int engine;
+	igt_spin_t *spin[16] = {};
+	uint32_t min[16], max[16];
+	int n, pmu;
+
+	get_freq(fd, 0, &min[0], &max[0]);
+
+	nengine = 0;
+	for_each_physical_engine(fd, engine) {
+		if (nengine == ARRAY_SIZE(engines))
+			break;
+
+		min[nengine] = min[0];
+		max[nengine] = max[0];
+		engines[nengine] = engine;
+		nengine++;
+	}
+	igt_require(nengine);
+
+	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
+	igt_require(pmu >= 0);
+
+	igt_until_timeout(timeout) {
+		uint32_t ctx, c_min, c_max;
+		igt_spin_t *kick;
+		double measured;
+
+		n = rand() % nengine;
+
+		ctx = gem_context_create(fd);
+		get_freq(fd, ctx, &c_min, &c_max);
+		c_min = rand() % (c_max - c_min) + c_min;
+		c_max = rand() % (c_max - c_min) + c_min;
+		set_freq(fd, ctx, c_min, c_max);
+		get_freq(fd, ctx, &c_min, &c_max);
+
+		igt_debug("Replacing (%d, %d) on engine %x with (%d, %d)\n",
+			  min[n], max[n], n, c_min, c_max);
+
+		kick = __igt_spin_batch_new(fd, ctx, engines[n], 0);
+		igt_spin_batch_free(fd, spin[n]);
+		spin[n] = kick;
+
+		gem_context_destroy(fd, ctx);
+
+		min[n] = c_min;
+		max[n] = c_max;
+
+		for (n = 0; n < nengine; n++) {
+			igt_debug("[%d]: [%d, %d]\n", n, min[n], max[n]);
+			if (min[n] < c_min)
+				c_min = min[n];
+			if (max[n] > c_max)
+				c_max = max[n];
+		}
+		igt_assert(c_max >= c_min);
+
+		usleep(50000);
+		measured = measure_frequency(pmu, 50000);
+
+		igt_debugfs_dump(fd, "i915_rps_boost_info");
+		igt_info("Measured %.1fMHz, expected [%d, %d]Mhz\n",
+			 measured, c_min, c_max);
+		igt_assert(measured > c_min - 100 && measured < c_max + 100);
+
+		/* Kick every engine to avoid hangcheck (bad rng) */
+		for (n = 0; n < nengine; n++) {
+			ctx = gem_context_create(fd);
+			set_freq(fd, ctx, min[n], max[n]);
+
+			kick = __igt_spin_batch_new(fd, ctx, engines[n], 0);
+			igt_spin_batch_free(fd, spin[n]);
+			spin[n] = kick;
+
+			gem_context_destroy(fd, ctx);
+		}
+	}
+
+	for (n = 0; n < nengine; n++)
+		igt_spin_batch_free(fd, spin[n]);
+	gem_quiescent_gpu(fd);
+
+	close(pmu);
+}
+
+static void invalid_param(int fd)
+{
+	uint32_t min, max;
+	uint32_t cur_min, cur_max;
+
+	get_freq(fd, 0, &min, &max);
+
+	igt_assert_eq(__set_freq(fd, 0, min - 50, max), -EINVAL);
+	igt_assert_eq(__set_freq(fd, 0, min, max + 50), -EINVAL);
+	igt_assert_eq(__set_freq(fd, 0, min + 50, min), -EINVAL);
+	igt_assert_eq(__set_freq(fd, 0, max, max - 50), -EINVAL);
+
+	get_freq(fd, 0, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+}
+
+static void idempotent(int fd)
+{
+	uint32_t min, max;
+	uint32_t cur_min, cur_max;
+
+	get_freq(fd, 0, &min, &max);
+
+	set_freq(fd, 0, max, max);
+	get_freq(fd, 0, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, max);
+	igt_assert_eq(cur_max, max);
+
+	set_freq(fd, 0, min, min);
+	get_freq(fd, 0, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, min);
+
+	set_freq(fd, 0, min, max);
+	get_freq(fd, 0, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+}
+
+static void independent(int fd)
+{
+	uint32_t min, max;
+	uint32_t cur_min, cur_max;
+	uint32_t ctx[2];
+
+	get_freq(fd, 0, &min, &max);
+
+	set_freq(fd, 0, max, max);
+	ctx[0] = gem_context_create(fd);
+	get_freq(fd, ctx[0], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+
+	set_freq(fd, 0, min, min);
+	get_freq(fd, ctx[0], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+
+	ctx[1] = gem_context_create(fd);
+	get_freq(fd, ctx[1], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+
+	set_freq(fd, ctx[1], max, max);
+	get_freq(fd, ctx[0], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+
+	get_freq(fd, 0, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, min);
+
+	get_freq(fd, ctx[1], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, max);
+	igt_assert_eq(cur_max, max);
+	gem_context_destroy(fd, ctx[1]);
+
+	get_freq(fd, ctx[0], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+	gem_context_destroy(fd, ctx[0]);
+}
+
+static bool has_ctx_freq(int fd)
+{
+	struct drm_i915_gem_context_param param = {
+		.param = LOCAL_CONTEXT_PARAM_FREQUENCY,
+	};
+
+	return __gem_context_get_param(fd, &param) == 0;
+}
+
+igt_main
+{
+	const struct intel_execution_engine *e;
+	int fd = -1;
+
+	igt_fixture {
+		fd = drm_open_driver(DRIVER_INTEL);
+		igt_require_gem(fd);
+
+		igt_require(has_ctx_freq(fd));
+	}
+
+	igt_subtest("invalid")
+		invalid_param(fd);
+
+	igt_subtest("idempotent")
+		idempotent(fd);
+
+	igt_subtest("independent")
+		independent(fd);
+
+	igt_skip_on_simulation();
+
+	for (e = intel_execution_engines; e->name; e++) {
+		if (e->exec_id == 0)
+			continue;
+
+		igt_subtest_group {
+			igt_fixture {
+				igt_require(gem_ring_has_physical_engine(fd, e->exec_id | e->flags));
+			}
+
+			igt_subtest(e->name)
+				single(fd, e);
+			igt_subtest_f("%s-inflight", e->name)
+				inflight(fd, e);
+		}
+	}
+
+	igt_subtest("sandwich")
+		sandwich(fd);
+
+	igt_subtest("smoketest")
+		smoketest(fd, 20);
+}
diff --git a/tests/meson.build b/tests/meson.build
index 58729231..f1271274 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -35,6 +35,7 @@ test_progs = [
 	'gem_ctx_bad_exec',
 	'gem_ctx_create',
 	'gem_ctx_exec',
+	'gem_ctx_freq',
 	'gem_ctx_param',
 	'gem_ctx_switch',
 	'gem_ctx_thrash',
-- 
2.16.2

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

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

* ✓ Fi.CI.BAT: success for igt: Add gem_ctx_freq to exercise requesting freq on a ctx
  2018-03-07 22:49 ` [igt-dev] " Chris Wilson
                   ` (6 preceding siblings ...)
  (?)
@ 2018-03-08  2:26 ` Patchwork
  -1 siblings, 0 replies; 36+ messages in thread
From: Patchwork @ 2018-03-08  2:26 UTC (permalink / raw)
  To: Chris Wilson; +Cc: intel-gfx

== Series Details ==

Series: igt: Add gem_ctx_freq to exercise requesting freq on a ctx
URL   : https://patchwork.freedesktop.org/series/39571/
State : success

== Summary ==

IGT patchset tested on top of latest successful build
b4689dce36d0fbd9aec70d5a4b077c43a6b9c254 igt: Remove gen7_forcewake_mt

with latest DRM-Tip kernel build CI_DRM_3892
fc93d196b8e4 drm-tip: 2018y-03m-07d-23h-55m-21s UTC integration manifest

Testlist changes:
+igt@gem_ctx_freq@blt
+igt@gem_ctx_freq@blt-inflight
+igt@gem_ctx_freq@bsd
+igt@gem_ctx_freq@bsd1
+igt@gem_ctx_freq@bsd1-inflight
+igt@gem_ctx_freq@bsd2
+igt@gem_ctx_freq@bsd2-inflight
+igt@gem_ctx_freq@bsd-inflight
+igt@gem_ctx_freq@idempotent
+igt@gem_ctx_freq@independent
+igt@gem_ctx_freq@invalid
+igt@gem_ctx_freq@render
+igt@gem_ctx_freq@render-inflight
+igt@gem_ctx_freq@sandwich
+igt@gem_ctx_freq@smoketest
+igt@gem_ctx_freq@vebox
+igt@gem_ctx_freq@vebox-inflight

---- Known issues:

Test gem_mmap_gtt:
        Subgroup basic-small-bo-tiledx:
                fail       -> PASS       (fi-gdg-551) fdo#102575
Test prime_vgem:
        Subgroup basic-fence-flip:
                pass       -> FAIL       (fi-byt-j1900) fdo#104008

fdo#102575 https://bugs.freedesktop.org/show_bug.cgi?id=102575
fdo#104008 https://bugs.freedesktop.org/show_bug.cgi?id=104008

fi-bdw-5557u     total:288  pass:267  dwarn:0   dfail:0   fail:0   skip:21  time:426s
fi-bdw-gvtdvm    total:288  pass:264  dwarn:0   dfail:0   fail:0   skip:24  time:428s
fi-blb-e6850     total:288  pass:223  dwarn:1   dfail:0   fail:0   skip:64  time:379s
fi-bsw-n3050     total:288  pass:242  dwarn:0   dfail:0   fail:0   skip:46  time:504s
fi-bwr-2160      total:288  pass:183  dwarn:0   dfail:0   fail:0   skip:105 time:280s
fi-bxt-dsi       total:288  pass:258  dwarn:0   dfail:0   fail:0   skip:30  time:488s
fi-bxt-j4205     total:288  pass:259  dwarn:0   dfail:0   fail:0   skip:29  time:492s
fi-byt-j1900     total:288  pass:252  dwarn:0   dfail:0   fail:1   skip:35  time:485s
fi-byt-n2820     total:288  pass:249  dwarn:0   dfail:0   fail:0   skip:39  time:474s
fi-cfl-8700k     total:288  pass:260  dwarn:0   dfail:0   fail:0   skip:28  time:410s
fi-cfl-s2        total:288  pass:262  dwarn:0   dfail:0   fail:0   skip:26  time:575s
fi-cfl-u         total:288  pass:262  dwarn:0   dfail:0   fail:0   skip:26  time:509s
fi-elk-e7500     total:288  pass:229  dwarn:0   dfail:0   fail:0   skip:59  time:416s
fi-gdg-551       total:288  pass:180  dwarn:0   dfail:0   fail:0   skip:108 time:290s
fi-glk-1         total:288  pass:260  dwarn:0   dfail:0   fail:0   skip:28  time:528s
fi-hsw-4770      total:288  pass:261  dwarn:0   dfail:0   fail:0   skip:27  time:398s
fi-ilk-650       total:288  pass:228  dwarn:0   dfail:0   fail:0   skip:60  time:412s
fi-ivb-3520m     total:288  pass:259  dwarn:0   dfail:0   fail:0   skip:29  time:456s
fi-ivb-3770      total:288  pass:255  dwarn:0   dfail:0   fail:0   skip:33  time:421s
fi-kbl-7500u     total:288  pass:263  dwarn:1   dfail:0   fail:0   skip:24  time:472s
fi-kbl-7567u     total:288  pass:268  dwarn:0   dfail:0   fail:0   skip:20  time:460s
fi-kbl-r         total:288  pass:261  dwarn:0   dfail:0   fail:0   skip:27  time:505s
fi-pnv-d510      total:288  pass:222  dwarn:1   dfail:0   fail:0   skip:65  time:585s
fi-skl-6260u     total:288  pass:268  dwarn:0   dfail:0   fail:0   skip:20  time:432s
fi-skl-6600u     total:288  pass:261  dwarn:0   dfail:0   fail:0   skip:27  time:522s
fi-skl-6700hq    total:288  pass:262  dwarn:0   dfail:0   fail:0   skip:26  time:538s
fi-skl-6700k2    total:288  pass:264  dwarn:0   dfail:0   fail:0   skip:24  time:501s
fi-skl-6770hq    total:288  pass:268  dwarn:0   dfail:0   fail:0   skip:20  time:493s
fi-skl-guc       total:288  pass:260  dwarn:0   dfail:0   fail:0   skip:28  time:420s
fi-skl-gvtdvm    total:288  pass:265  dwarn:0   dfail:0   fail:0   skip:23  time:428s
fi-snb-2520m     total:288  pass:248  dwarn:0   dfail:0   fail:0   skip:40  time:530s
fi-snb-2600      total:288  pass:248  dwarn:0   dfail:0   fail:0   skip:40  time:392s

== Logs ==

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

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

* ✓ Fi.CI.IGT: success for igt: Add gem_ctx_freq to exercise requesting freq on a ctx
  2018-03-07 22:49 ` [igt-dev] " Chris Wilson
                   ` (7 preceding siblings ...)
  (?)
@ 2018-03-08  3:12 ` Patchwork
  -1 siblings, 0 replies; 36+ messages in thread
From: Patchwork @ 2018-03-08  3:12 UTC (permalink / raw)
  To: Chris Wilson; +Cc: intel-gfx

== Series Details ==

Series: igt: Add gem_ctx_freq to exercise requesting freq on a ctx
URL   : https://patchwork.freedesktop.org/series/39571/
State : success

== Summary ==

---- Known issues:

Test gem_softpin:
        Subgroup noreloc-s3:
                skip       -> PASS       (shard-snb) fdo#103375
Test kms_chv_cursor_fail:
        Subgroup pipe-b-128x128-right-edge:
                pass       -> DMESG-WARN (shard-snb) fdo#105185 +2
Test kms_flip:
        Subgroup 2x-plain-flip-fb-recreate:
                fail       -> PASS       (shard-hsw) fdo#100368 +1
Test kms_frontbuffer_tracking:
        Subgroup fbc-modesetfrombusy:
                pass       -> FAIL       (shard-apl) fdo#103167
        Subgroup fbc-suspend:
                fail       -> PASS       (shard-apl) fdo#101623
Test kms_rotation_crc:
        Subgroup primary-rotation-180:
                fail       -> PASS       (shard-snb) fdo#103925
Test kms_sysfs_edid_timing:
                warn       -> PASS       (shard-apl) fdo#100047
Test pm_lpsp:
        Subgroup screens-disabled:
                fail       -> PASS       (shard-hsw) fdo#104941

fdo#103375 https://bugs.freedesktop.org/show_bug.cgi?id=103375
fdo#105185 https://bugs.freedesktop.org/show_bug.cgi?id=105185
fdo#100368 https://bugs.freedesktop.org/show_bug.cgi?id=100368
fdo#103167 https://bugs.freedesktop.org/show_bug.cgi?id=103167
fdo#101623 https://bugs.freedesktop.org/show_bug.cgi?id=101623
fdo#103925 https://bugs.freedesktop.org/show_bug.cgi?id=103925
fdo#100047 https://bugs.freedesktop.org/show_bug.cgi?id=100047
fdo#104941 https://bugs.freedesktop.org/show_bug.cgi?id=104941

shard-apl        total:3448 pass:1806 dwarn:1   dfail:0   fail:8   skip:1632 time:12072s
shard-hsw        total:3484 pass:1772 dwarn:1   dfail:0   fail:2   skip:1708 time:11921s
shard-snb        total:3484 pass:1364 dwarn:2   dfail:0   fail:1   skip:2117 time:7142s
Blacklisted hosts:
shard-kbl        total:3328 pass:1791 dwarn:7   dfail:1   fail:7   skip:1519 time:8399s

== Logs ==

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

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

* [PATCH igt v2] igt: Add gem_ctx_freq to exercise requesting freq on a ctx
  2018-03-07 22:49 ` [igt-dev] " Chris Wilson
                   ` (8 preceding siblings ...)
  (?)
@ 2018-03-08  9:02 ` Chris Wilson
  -1 siblings, 0 replies; 36+ messages in thread
From: Chris Wilson @ 2018-03-08  9:02 UTC (permalink / raw)
  To: intel-gfx

Exercise some new API that allows applications to request that
individual contexts are executed within a desired frequency range.

v2: Split single/continuous set_freq subtests

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

diff --git a/tests/Makefile.am b/tests/Makefile.am
index dbc7be72..389f7fc7 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -104,6 +104,7 @@ drm_import_export_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 drm_import_export_LDADD = $(LDADD) -lpthread
 gem_close_race_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_close_race_LDADD = $(LDADD) -lpthread
+gem_ctx_freq_LDADD = $(LDADD) $(top_builddir)/lib/libigt_perf.la
 gem_ctx_thrash_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_ctx_thrash_LDADD = $(LDADD) -lpthread
 gem_exec_parallel_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index 4a81ac4a..3d079c42 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -58,6 +58,7 @@ TESTS_progs = \
 	gem_ctx_bad_exec \
 	gem_ctx_create \
 	gem_ctx_exec \
+	gem_ctx_freq \
 	gem_ctx_isolation \
 	gem_ctx_param \
 	gem_ctx_switch \
diff --git a/tests/gem_ctx_freq.c b/tests/gem_ctx_freq.c
new file mode 100644
index 00000000..f7e79ac3
--- /dev/null
+++ b/tests/gem_ctx_freq.c
@@ -0,0 +1,604 @@
+/*
+ * 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 <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 <time.h>
+
+#include "igt.h"
+#include "igt_perf.h"
+
+#define LOCAL_CONTEXT_PARAM_FREQUENCY 8
+
+#define SAMPLE_PERIOD (USEC_PER_SEC / 10)
+
+static int __set_freq(int fd, uint32_t ctx, uint32_t min, uint32_t max)
+{
+	struct drm_i915_gem_context_param param = {
+		.ctx_id = ctx,
+		.param = LOCAL_CONTEXT_PARAM_FREQUENCY,
+		.value = (uint64_t)max << 32 | min,
+	};
+
+	return __gem_context_set_param(fd, &param);
+}
+
+static void set_freq(int fd, uint32_t ctx, uint32_t min, uint32_t max)
+{
+	igt_assert_eq(__set_freq(fd, ctx, min, max), 0);
+}
+
+static void get_freq(int fd, uint32_t ctx, uint32_t *min, uint32_t *max)
+{
+	struct drm_i915_gem_context_param param = {
+		.ctx_id = ctx,
+		.param = LOCAL_CONTEXT_PARAM_FREQUENCY,
+	};
+
+	gem_context_get_param(fd, &param);
+
+	*min = param.value & 0xffffffff;
+	*max = param.value >> 32;
+}
+
+static double measure_frequency(int pmu, int period_us)
+{
+	uint64_t data[2];
+	uint64_t d_t, d_v;
+
+	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 * 1e9 / d_t;
+}
+
+static void single(int fd, const struct intel_execution_engine *e)
+{
+	const unsigned int engine = e->exec_id | e->flags;
+	uint32_t ctx = gem_context_create(fd);
+	uint32_t min, max;
+	double measured;
+	igt_spin_t *spin;
+	int pmu;
+
+	get_freq(fd, ctx, &min, &max);
+	igt_info("Min freq: %dMHz; Max freq: %dMHz\n", min, max);
+
+	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
+	igt_require(pmu >= 0);
+
+	for (uint32_t freq = min + 50; freq <= max; freq += 100) {
+		uint32_t cur, discard;
+
+		set_freq(fd, ctx, freq, freq);
+		get_freq(fd, ctx, &cur, &discard);
+
+		gem_quiescent_gpu(fd);
+		spin = __igt_spin_batch_new(fd, ctx, engine, 0);
+		usleep(10000);
+
+		measured = measure_frequency(pmu, SAMPLE_PERIOD);
+		igt_debugfs_dump(fd, "i915_rps_boost_info");
+
+		igt_spin_batch_free(fd, spin);
+		igt_info("%s(single): Measured %.1fMHz, expected %dMhz\n",
+			 e->name, measured, cur);
+		igt_assert(measured > cur - 100 && measured < cur + 100);
+	}
+	gem_quiescent_gpu(fd);
+
+	close(pmu);
+	gem_context_destroy(fd, ctx);
+}
+
+static void continuous(int fd, const struct intel_execution_engine *e)
+{
+	const unsigned int engine = e->exec_id | e->flags;
+	uint32_t ctx = gem_context_create(fd);
+	uint32_t min, max;
+	double measured;
+	igt_spin_t *spin;
+	int pmu;
+
+	get_freq(fd, ctx, &min, &max);
+	igt_info("Min freq: %dMHz; Max freq: %dMHz\n", min, max);
+
+	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
+	igt_require(pmu >= 0);
+
+	gem_quiescent_gpu(fd);
+	spin = __igt_spin_batch_new(fd, ctx, engine, 0);
+	for (uint32_t freq = min + 50; freq <= max; freq += 100) {
+		uint32_t cur, discard;
+		igt_spin_t *kick;
+
+		set_freq(fd, ctx, freq, freq);
+		get_freq(fd, ctx, &cur, &discard);
+
+		/*
+		 * When requesting a new frequency on the currently
+		 * executing context, it does not take effect until the
+		 * next context switch. In this case, we trigger a lite
+		 * restore.
+		 */
+		kick = __igt_spin_batch_new(fd, ctx, engine, 0);
+		igt_spin_batch_free(fd, spin);
+		spin = kick;
+
+		usleep(10000);
+
+		measured = measure_frequency(pmu, SAMPLE_PERIOD);
+		igt_debugfs_dump(fd, "i915_rps_boost_info");
+
+		igt_info("%s(continuous): Measured %.1fMHz, expected %dMhz\n",
+			 e->name, measured, cur);
+		igt_assert(measured > cur - 100 && measured < cur + 100);
+	}
+	igt_spin_batch_free(fd, spin);
+	gem_quiescent_gpu(fd);
+
+	close(pmu);
+	gem_context_destroy(fd, ctx);
+}
+
+static void inflight(int fd, const struct intel_execution_engine *e)
+{
+	const unsigned int engine = e->exec_id | e->flags;
+	uint32_t ctx, min, max, freq, discard;
+	double measured;
+	igt_spin_t *plug, *work[2];
+	int pmu;
+
+	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
+	igt_require(pmu >= 0);
+
+	ctx = gem_context_create(fd);
+	get_freq(fd, ctx, &min, &max);
+	set_freq(fd, ctx, min, min);
+
+	igt_info("Min freq: %dMHz; Max freq: %dMHz\n", min, max);
+
+	gem_quiescent_gpu(fd);
+	plug = igt_spin_batch_new(fd, ctx, engine, 0);
+	gem_context_destroy(fd, ctx);
+	for (int n = 0; n < 16; n++) {
+		struct drm_i915_gem_exec_object2 obj = {
+			.handle = plug->handle,
+		};
+		struct drm_i915_gem_execbuffer2 eb = {
+			.buffer_count = 1,
+			.buffers_ptr = to_user_pointer(&obj),
+			.flags = engine,
+			.rsvd1 = gem_context_create(fd),
+		};
+		set_freq(fd, eb.rsvd1, min, min);
+		gem_execbuf(fd, &eb);
+		gem_context_destroy(fd, eb.rsvd1);
+	}
+	measured = measure_frequency(pmu, SAMPLE_PERIOD);
+	igt_debugfs_dump(fd, "i915_rps_boost_info");
+	igt_info("%s(plug): Measured %.1fMHz, expected %dMhz\n",
+		 e->name, measured, min);
+	igt_assert(measured > min - 100 && measured < min + 100);
+
+	ctx = gem_context_create(fd);
+	set_freq(fd, ctx, max, max);
+	work[0] = __igt_spin_batch_new(fd, ctx, engine, 0);
+
+	/* work is now queued but not executing */
+	freq = (max + min) / 2;
+	set_freq(fd, ctx, freq, freq);
+	get_freq(fd, ctx, &freq, &discard);
+	gem_context_destroy(fd, ctx);
+
+	ctx = gem_context_create(fd);
+	set_freq(fd, ctx, max, max);
+	work[1] = __igt_spin_batch_new(fd, ctx, engine, 0);
+	gem_context_destroy(fd, ctx);
+
+	igt_spin_batch_end(plug);
+	do
+		usleep(10000);
+	while (gem_bo_busy(fd, plug->handle));
+	igt_spin_batch_free(fd, plug);
+
+	/* Now work will execute */
+	measured = measure_frequency(pmu, SAMPLE_PERIOD);
+	igt_debugfs_dump(fd, "i915_engine_info");
+	igt_debugfs_dump(fd, "i915_rps_boost_info");
+	igt_info("%s(work0): Measured %.1fMHz, expected %dMhz\n",
+		 e->name, measured, freq);
+	igt_assert(measured > freq - 100 && measured < freq + 100);
+
+	igt_spin_batch_end(work[0]);
+	do
+		usleep(10000);
+	while (gem_bo_busy(fd, work[0]->handle));
+	igt_spin_batch_free(fd, work[0]);
+
+	measured = measure_frequency(pmu, SAMPLE_PERIOD);
+	igt_debugfs_dump(fd, "i915_engine_info");
+	igt_debugfs_dump(fd, "i915_rps_boost_info");
+	igt_info("%s(work1): Measured %.1fMHz, expected %dMhz\n",
+		 e->name, measured, max);
+	igt_assert(measured > max - 100 && measured < max + 100);
+
+	igt_spin_batch_free(fd, work[1]);
+	close(pmu);
+	gem_quiescent_gpu(fd);
+}
+
+static void sandwich(int fd)
+{
+	uint32_t ctx = gem_context_create(fd);
+	unsigned int engine;
+	uint32_t min, max;
+	igt_spin_t *spin;
+	int pmu;
+
+	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
+	igt_require(pmu >= 0);
+
+	spin = igt_spin_batch_new(fd, ctx, 0, 0);
+	get_freq(fd, ctx, &min, &max);
+	set_freq(fd, ctx, min, min);
+	for_each_physical_engine(fd, engine) {
+		struct drm_i915_gem_exec_object2 obj = {
+			.handle = spin->handle,
+		};
+		struct drm_i915_gem_execbuffer2 eb = {
+			.buffer_count = 1,
+			.buffers_ptr = to_user_pointer(&obj),
+			.flags = engine,
+			.rsvd1 = ctx,
+		};
+		uint32_t cur, discard;
+		double measured;
+
+		min += 50;
+		if (min > max)
+			break;
+
+		set_freq(fd, ctx, min, min);
+		get_freq(fd, ctx, &cur, &discard);
+
+		gem_execbuf(fd, &eb);
+		usleep(10000);
+
+		measured = measure_frequency(pmu, SAMPLE_PERIOD);
+		igt_debugfs_dump(fd, "i915_rps_boost_info");
+
+		igt_info("Measured %.1fMHz, expected %dMhz\n", measured, cur);
+		igt_assert(measured > cur - 100 && measured < cur + 100);
+	}
+	igt_spin_batch_free(fd, spin);
+	gem_quiescent_gpu(fd);
+
+	gem_context_destroy(fd, ctx);
+	close(pmu);
+}
+
+static void pwm(int fd, unsigned int *engines, unsigned int nengine, int link)
+{
+	uint32_t ctx[nengine];
+
+	fcntl(link, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
+
+	for (unsigned int n = 0; n < nengine; n++)
+		ctx[n] = gem_context_create(fd);
+
+	do {
+		igt_spin_t *spin;
+		struct {
+			uint32_t engine;
+			uint32_t min;
+			uint32_t max;
+		} req;
+
+		while (read(link, &req, sizeof(req)) > 0) {
+			if ((req.engine | req.min | req.max) == 0)
+				goto out;
+
+			igt_assert(req.engine < nengine);
+			set_freq(fd, ctx[req.engine], req.min, req.max);
+		}
+
+		/* Create a 20% load using busy spinners */
+		spin = __igt_spin_batch_new(fd, ctx[0], engines[0], 0);
+		for (unsigned int n = 1; n < nengine; n++) {
+			struct drm_i915_gem_exec_object2 obj = {
+				.handle = spin->handle,
+			};
+			struct drm_i915_gem_execbuffer2 eb = {
+				.buffer_count = 1,
+				.buffers_ptr = to_user_pointer(&obj),
+				.flags = engines[n],
+				.rsvd1 = ctx[n],
+			};
+			gem_execbuf(fd, &eb);
+		}
+		usleep(100);
+		igt_spin_batch_end(spin);
+
+		do
+			usleep(10);
+		while (gem_bo_busy(fd, spin->handle));
+		igt_spin_batch_free(fd, spin);
+		usleep(400);
+	} while (1);
+
+out:
+	for (unsigned int n = 0; n < nengine; n++)
+		gem_context_destroy(fd, ctx[n]);
+}
+
+static void smoketest(int fd, int timeout)
+{
+	unsigned int engines[16];
+	unsigned int nengine;
+	unsigned int engine;
+	uint32_t min[16], max[16];
+	int pmu, link[2];
+
+	get_freq(fd, 0, &min[0], &max[0]);
+
+	nengine = 0;
+	for_each_physical_engine(fd, engine) {
+		if (nengine == ARRAY_SIZE(engines) - 1)
+			break;
+
+		min[nengine] = min[0];
+		max[nengine] = max[0];
+		engines[nengine] = engine;
+		nengine++;
+	}
+	igt_require(nengine);
+
+	igt_assert(pipe(link) == 0);
+	igt_fork(child, 1)
+		pwm(fd, engines, nengine, link[0]);
+	close(link[0]);
+
+	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
+	igt_require(pmu >= 0);
+
+	igt_until_timeout(timeout) {
+		struct {
+			uint32_t engine;
+			uint32_t min;
+			uint32_t max;
+		} req;
+		double measured;
+		uint32_t ctx;
+
+		req.engine = rand() % nengine;
+
+		ctx = gem_context_create(fd);
+		get_freq(fd, ctx, &req.min, &req.max);
+		req.min = rand() % (req.max - req.min) + req.min;
+		req.max = rand() % (req.max - req.min) + req.min;
+		set_freq(fd, ctx, req.min, req.max);
+		get_freq(fd, ctx, &req.min, &req.max);
+
+		igt_debug("Replacing (%d, %d) on engine %x with (%d, %d)\n",
+			  min[req.engine], max[req.engine], req.engine,
+			  req.min, req.max);
+		igt_assert(write(link[1], &req, sizeof(req)) == sizeof(req));
+		gem_context_destroy(fd, ctx);
+
+		min[req.engine] = req.min;
+		max[req.engine] = req.max;
+
+		for (unsigned int n = 0; n < nengine; n++) {
+			igt_debug("[%d]: [%d, %d]\n", n, min[n], max[n]);
+			if (min[n] < req.min)
+				req.min = min[n];
+			if (max[n] > req.max)
+				req.max = max[n];
+		}
+		igt_assert(req.max >= req.min);
+
+		usleep(50000);
+		measured = measure_frequency(pmu, SAMPLE_PERIOD);
+
+		if (measured <= req.min - 100 || measured >= req.max + 100)
+			igt_debugfs_dump(fd, "i915_rps_boost_info");
+		igt_info("Measured %.1fMHz, expected [%d, %d]Mhz\n",
+			 measured, req.min, req.max);
+		igt_assert(measured > req.min - 100 &&
+			   measured < req.max + 100);
+	}
+
+	do {
+		struct {
+			uint32_t engine;
+			uint32_t min;
+			uint32_t max;
+		} req = {};
+
+		write(link[1], &req, sizeof(req));
+		close(link[1]);
+	} while (0);
+	igt_waitchildren();
+	gem_quiescent_gpu(fd);
+
+	close(pmu);
+}
+
+static void invalid_param(int fd)
+{
+	uint32_t min, max;
+	uint32_t cur_min, cur_max;
+
+	get_freq(fd, 0, &min, &max);
+
+	igt_assert_eq(__set_freq(fd, 0, min - 50, max), -EINVAL);
+	igt_assert_eq(__set_freq(fd, 0, min, max + 50), -EINVAL);
+	igt_assert_eq(__set_freq(fd, 0, min + 50, min), -EINVAL);
+	igt_assert_eq(__set_freq(fd, 0, max, max - 50), -EINVAL);
+
+	get_freq(fd, 0, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+}
+
+static void idempotent(int fd)
+{
+	uint32_t min, max;
+	uint32_t cur_min, cur_max;
+
+	get_freq(fd, 0, &min, &max);
+
+	set_freq(fd, 0, max, max);
+	get_freq(fd, 0, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, max);
+	igt_assert_eq(cur_max, max);
+
+	set_freq(fd, 0, min, min);
+	get_freq(fd, 0, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, min);
+
+	set_freq(fd, 0, min, max);
+	get_freq(fd, 0, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+}
+
+static void independent(int fd)
+{
+	uint32_t min, max;
+	uint32_t cur_min, cur_max;
+	uint32_t ctx[2];
+
+	get_freq(fd, 0, &min, &max);
+
+	set_freq(fd, 0, max, max);
+	ctx[0] = gem_context_create(fd);
+	get_freq(fd, ctx[0], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+
+	set_freq(fd, 0, min, min);
+	get_freq(fd, ctx[0], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+
+	ctx[1] = gem_context_create(fd);
+	get_freq(fd, ctx[1], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+
+	set_freq(fd, ctx[1], max, max);
+	get_freq(fd, ctx[0], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+
+	get_freq(fd, 0, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, min);
+
+	get_freq(fd, ctx[1], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, max);
+	igt_assert_eq(cur_max, max);
+	gem_context_destroy(fd, ctx[1]);
+
+	get_freq(fd, ctx[0], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+	gem_context_destroy(fd, ctx[0]);
+}
+
+static bool has_ctx_freq(int fd)
+{
+	struct drm_i915_gem_context_param param = {
+		.param = LOCAL_CONTEXT_PARAM_FREQUENCY,
+	};
+
+	return __gem_context_get_param(fd, &param) == 0;
+}
+
+igt_main
+{
+	const struct intel_execution_engine *e;
+	int fd = -1;
+
+	igt_fixture {
+		fd = drm_open_driver(DRIVER_INTEL);
+		igt_require_gem(fd);
+
+		igt_require(has_ctx_freq(fd));
+	}
+
+	igt_subtest("invalid")
+		invalid_param(fd);
+
+	igt_subtest("idempotent")
+		idempotent(fd);
+
+	igt_subtest("independent")
+		independent(fd);
+
+	igt_skip_on_simulation();
+
+	for (e = intel_execution_engines; e->name; e++) {
+		if (e->exec_id == 0)
+			continue;
+
+		igt_subtest_group {
+			igt_fixture {
+				igt_require(gem_ring_has_physical_engine(fd, e->exec_id | e->flags));
+			}
+
+			igt_subtest_f("%s-single", e->name)
+				single(fd, e);
+			igt_subtest_f("%s-continuous", e->name)
+				continuous(fd, e);
+			igt_subtest_f("%s-inflight", e->name)
+				inflight(fd, e);
+		}
+	}
+
+	igt_subtest("sandwich")
+		sandwich(fd);
+
+	igt_subtest("smoketest")
+		smoketest(fd, 20);
+}
diff --git a/tests/meson.build b/tests/meson.build
index 58729231..f1271274 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -35,6 +35,7 @@ test_progs = [
 	'gem_ctx_bad_exec',
 	'gem_ctx_create',
 	'gem_ctx_exec',
+	'gem_ctx_freq',
 	'gem_ctx_param',
 	'gem_ctx_switch',
 	'gem_ctx_thrash',
-- 
2.16.2

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

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

* Re: [PATCH igt] igt: Add gem_ctx_freq to exercise requesting freq on a ctx
  2018-03-08  1:18     ` [igt-dev] [Intel-gfx] " Chris Wilson
@ 2018-03-08 17:33       ` Antonio Argenziano
  -1 siblings, 0 replies; 36+ messages in thread
From: Antonio Argenziano @ 2018-03-08 17:33 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx; +Cc: igt-dev



On 07/03/18 17:18, Chris Wilson wrote:
> Quoting Antonio Argenziano (2018-03-08 00:55:47)
>>
>>
>> On 07/03/18 14:49, Chris Wilson wrote:
>>> +static void single(int fd, const struct intel_execution_engine *e)
>>> +{
>>> +     const unsigned int engine = e->exec_id | e->flags;
>>> +     uint32_t ctx = gem_context_create(fd);
>>> +     uint32_t min, max;
>>> +     double measured;
>>> +     igt_spin_t *spin;
>>> +     int pmu;
>>> +
>>> +     get_freq(fd, ctx, &min, &max);
>>> +     igt_info("Min freq: %dMHz; Max freq: %dMHz\n", min, max);
>>> +
>>> +     pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
>>> +     igt_require(pmu >= 0);
>>> +
>>> +     gem_quiescent_gpu(fd);
>>> +     measured = measure_frequency(pmu, 10000);
>>> +     igt_info("Initial (idle) freq: %.1fMHz\n",measured);
>>> +     igt_require(measured >= min - 50 && measured <= min + 50);
>>> +
>>> +     for (uint32_t freq = min + 50; freq <= max; freq += 100) {
>>> +             set_freq(fd, ctx, freq, freq);
>>> +
>>> +             gem_quiescent_gpu(fd);
>>> +             spin = __igt_spin_batch_new(fd, ctx, engine, 0);
>>> +             usleep(10000);
>>> +
>>> +             measured = measure_frequency(pmu, 50000);
>>> +             igt_debugfs_dump(fd, "i915_rps_boost_info");
>>> +
>>> +             igt_spin_batch_free(fd, spin);
>>> +             igt_info("%s(single): Measured %.1fMHz, expected %dMhz\n",
>>> +                      e->name, measured, freq);
>>> +             igt_assert(measured > freq - 100 && measured < freq + 100);
>>> +     }
>>> +     gem_quiescent_gpu(fd);
>>
>> Check frequency has gone back to ~min.
> 
> It's not that interesting a test (covered already by pmu) as we
> essentially lie anyway over idle.

Agreed.

> 
>> I would suggest to split here into two sub-tests.
>>
>>> +     spin = __igt_spin_batch_new(fd, ctx, engine, 0);
>>> +     for (uint32_t freq = min + 50; freq <= max; freq += 100) {
>>> +             igt_spin_t *kick;
>>> +
>>> +             set_freq(fd, ctx, freq, freq);
>>> +
>>> +             /*
>>> +              * When requesting a new frequency on the currently
>>> +              * executing context, it does not take effect until the
>>> +              * next context switch. In this case, we trigger a lite
>>> +              * restore.
>>
>> Is this enforced by the ABI?
> 
> Enforced? No. The comment is precisely because it's not checked on
> calling whether the context is currently on the HW and trying hard to be
> sure that no one expects us to do that check. i.e. that set_freq()
> doesn't change frequency itself, but doesn't rule it out either as it
> may appear to have that effect due to many external factors.

That is what I thought :).

I see that you had a new version with more tests, I'll have a look at that.

Thanks,
Antonio

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

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

* Re: [igt-dev] [Intel-gfx] [PATCH igt] igt: Add gem_ctx_freq to exercise requesting freq on a ctx
@ 2018-03-08 17:33       ` Antonio Argenziano
  0 siblings, 0 replies; 36+ messages in thread
From: Antonio Argenziano @ 2018-03-08 17:33 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx; +Cc: igt-dev



On 07/03/18 17:18, Chris Wilson wrote:
> Quoting Antonio Argenziano (2018-03-08 00:55:47)
>>
>>
>> On 07/03/18 14:49, Chris Wilson wrote:
>>> +static void single(int fd, const struct intel_execution_engine *e)
>>> +{
>>> +     const unsigned int engine = e->exec_id | e->flags;
>>> +     uint32_t ctx = gem_context_create(fd);
>>> +     uint32_t min, max;
>>> +     double measured;
>>> +     igt_spin_t *spin;
>>> +     int pmu;
>>> +
>>> +     get_freq(fd, ctx, &min, &max);
>>> +     igt_info("Min freq: %dMHz; Max freq: %dMHz\n", min, max);
>>> +
>>> +     pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
>>> +     igt_require(pmu >= 0);
>>> +
>>> +     gem_quiescent_gpu(fd);
>>> +     measured = measure_frequency(pmu, 10000);
>>> +     igt_info("Initial (idle) freq: %.1fMHz\n",measured);
>>> +     igt_require(measured >= min - 50 && measured <= min + 50);
>>> +
>>> +     for (uint32_t freq = min + 50; freq <= max; freq += 100) {
>>> +             set_freq(fd, ctx, freq, freq);
>>> +
>>> +             gem_quiescent_gpu(fd);
>>> +             spin = __igt_spin_batch_new(fd, ctx, engine, 0);
>>> +             usleep(10000);
>>> +
>>> +             measured = measure_frequency(pmu, 50000);
>>> +             igt_debugfs_dump(fd, "i915_rps_boost_info");
>>> +
>>> +             igt_spin_batch_free(fd, spin);
>>> +             igt_info("%s(single): Measured %.1fMHz, expected %dMhz\n",
>>> +                      e->name, measured, freq);
>>> +             igt_assert(measured > freq - 100 && measured < freq + 100);
>>> +     }
>>> +     gem_quiescent_gpu(fd);
>>
>> Check frequency has gone back to ~min.
> 
> It's not that interesting a test (covered already by pmu) as we
> essentially lie anyway over idle.

Agreed.

> 
>> I would suggest to split here into two sub-tests.
>>
>>> +     spin = __igt_spin_batch_new(fd, ctx, engine, 0);
>>> +     for (uint32_t freq = min + 50; freq <= max; freq += 100) {
>>> +             igt_spin_t *kick;
>>> +
>>> +             set_freq(fd, ctx, freq, freq);
>>> +
>>> +             /*
>>> +              * When requesting a new frequency on the currently
>>> +              * executing context, it does not take effect until the
>>> +              * next context switch. In this case, we trigger a lite
>>> +              * restore.
>>
>> Is this enforced by the ABI?
> 
> Enforced? No. The comment is precisely because it's not checked on
> calling whether the context is currently on the HW and trying hard to be
> sure that no one expects us to do that check. i.e. that set_freq()
> doesn't change frequency itself, but doesn't rule it out either as it
> may appear to have that effect due to many external factors.

That is what I thought :).

I see that you had a new version with more tests, I'll have a look at that.

Thanks,
Antonio

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

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

* Re: [PATCH igt] igt: Add gem_ctx_freq to exercise requesting freq on a ctx
  2018-03-08 17:33       ` [igt-dev] [Intel-gfx] " Antonio Argenziano
@ 2018-03-08 17:39         ` Chris Wilson
  -1 siblings, 0 replies; 36+ messages in thread
From: Chris Wilson @ 2018-03-08 17:39 UTC (permalink / raw)
  To: Antonio Argenziano, intel-gfx; +Cc: igt-dev

Quoting Antonio Argenziano (2018-03-08 17:33:11)
> 
> 
> On 07/03/18 17:18, Chris Wilson wrote:
> > Quoting Antonio Argenziano (2018-03-08 00:55:47)
> >>
> >>
> >> On 07/03/18 14:49, Chris Wilson wrote:
> >>> +     gem_quiescent_gpu(fd);
> >>
> >> Check frequency has gone back to ~min.
> > 
> > It's not that interesting a test (covered already by pmu) as we
> > essentially lie anyway over idle.
> 
> Agreed.

I should mention somewhere the reason for all the gem_quiescent_gpu()
spam here is because I want to make sure that no stray waitboosts affect
the measurements.
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* Re: [igt-dev] [Intel-gfx] [PATCH igt] igt: Add gem_ctx_freq to exercise requesting freq on a ctx
@ 2018-03-08 17:39         ` Chris Wilson
  0 siblings, 0 replies; 36+ messages in thread
From: Chris Wilson @ 2018-03-08 17:39 UTC (permalink / raw)
  To: Antonio Argenziano, intel-gfx; +Cc: igt-dev

Quoting Antonio Argenziano (2018-03-08 17:33:11)
> 
> 
> On 07/03/18 17:18, Chris Wilson wrote:
> > Quoting Antonio Argenziano (2018-03-08 00:55:47)
> >>
> >>
> >> On 07/03/18 14:49, Chris Wilson wrote:
> >>> +     gem_quiescent_gpu(fd);
> >>
> >> Check frequency has gone back to ~min.
> > 
> > It's not that interesting a test (covered already by pmu) as we
> > essentially lie anyway over idle.
> 
> Agreed.

I should mention somewhere the reason for all the gem_quiescent_gpu()
spam here is because I want to make sure that no stray waitboosts affect
the measurements.
-Chris
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

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

* Re: [PATCH igt] igt: Add gem_ctx_freq to exercise requesting freq on a ctx
  2018-03-14  9:03     ` Chris Wilson
@ 2018-03-14  9:49       ` Sagar Arun Kamble
  0 siblings, 0 replies; 36+ messages in thread
From: Sagar Arun Kamble @ 2018-03-14  9:49 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx; +Cc: igt-dev, Praveen Paneri



On 3/14/2018 2:33 PM, Chris Wilson wrote:
> Quoting Sagar Arun Kamble (2018-03-14 08:15:15)
>>
>> On 3/13/2018 7:28 PM, Chris Wilson wrote:
>>> Exercise some new API that allows applications to request that
>>> individual contexts are executed within a desired frequency range.
>>>
>>> v2: Split single/continuous set_freq subtests
>>> v3: Do an up/down ramp for individual freq request, check nothing
>>> changes after each invalid request
>>> v4: Check the frequencies reported by the kernel across the entire
>>> range.
>>> v5: Rewrite sandwich to create a sandwich between multiple concurrent
>>> engines.
>>> v6: Exercise sysfs overrides.
>>> v7: Reset min/max of default context after independent(); don't ask
>>> about failure
>>> v8: Check transition beyond randomly chosen frequencies as well as
>>> up/down ramps.
>>>
>>> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
>>> Cc: Praveen Paneri <praveen.paneri@intel.com>
>>> Cc: Sagar A Kamble <sagar.a.kamble@intel.com>
>>> Cc: Antonio Argenziano <antonio.argenziano@intel.com>
>>> Reviewed-by: Antonio Argenziano <antonio.argenziano@intel.com> #v5
>> There are few stray whitespaces in __pmu_within_tolerance, pmu_assert.
>> Otherwise looks good to me.
>> Reviewed-by: Sagar Arun Kamble <sagar.a.kamble@intel.com>
>>
>> Can you please clarify few things below:
>>> ---
>> <snip>
>>> +
>>> +static void sysfs_clamp(int fd, const struct intel_execution_engine *e)
>>> +{
>>> +#define N_STEPS 10
>>> +     const unsigned int engine = e->exec_id | e->flags;
>>> +     uint32_t ctx = gem_context_create(fd);
>>> +     uint32_t sys_min, sys_max;
>>> +     uint32_t min, max;
>>> +     double measured;
>>> +     igt_spin_t *spin;
>>> +     int pmu;
>>> +
>>> +     get_sysfs_freq(&sys_min, &sys_max);
>>> +     igt_info("System min freq: %dMHz; max freq: %dMHz\n", sys_min, sys_max);
>>> +
>>> +     get_freq(fd, ctx, &min, &max);
>>> +     igt_info("Context min freq: %dMHz; max freq: %dMHz\n", min, max);
>>> +
>>> +     pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
>>> +     igt_require(pmu >= 0);
>>> +
>>> +     for (int outer = 0; outer <= 2*N_STEPS; outer++) {
>>> +             int ofrac = outer > N_STEPS ? 2*N_STEPS - outer : outer;
>>> +             uint32_t ofreq = min + (max - min) * ofrac / N_STEPS;
>>> +             uint32_t cur, discard;
>>> +
>>> +             for (int inner = 0; inner <= 2*N_STEPS; inner++) {
>>> +                     int ifrac = inner > N_STEPS ? 2*N_STEPS - inner : inner;
>>> +                     uint32_t ifreq = min + (max - min) * ifrac / N_STEPS;
>>> +
>>> +                     set_freq(fd, ctx, ifreq, ifreq);
>>> +
>>> +                     gem_quiescent_gpu(fd);
>>> +                     spin = __igt_spin_batch_new(fd, ctx, engine, 0);
>>> +                     usleep(10000);
>>> +
>>> +                     set_sysfs_freq(ofreq, ofreq);
>>> +                     get_sysfs_freq(&cur, &discard);
>> We don't sleep here because we know that we set the frequency in sysfs?
> sysfs is a synchronous interface, yes.
>
>>> +
>>> +                     measured = measure_frequency(pmu, SAMPLE_PERIOD);
>>> +                     igt_debugfs_dump(fd, "i915_rps_boost_info");
>>> +
>>> +                     set_sysfs_freq(sys_min, sys_max);
>>> +
>>> +                     igt_spin_batch_free(fd, spin);
>>> +                     igt_info("%s(sysfs): Measured %.1fMHz, context %dMhz, expected %dMhz\n",
>>> +                                     e->name, measured, ifreq, cur);
>>> +                     pmu_assert(measured, cur);
>>> +             }
>>> +     }
>>> +     gem_quiescent_gpu(fd);
>>> +
>>> +     close(pmu);
>>> +     gem_context_destroy(fd, ctx);
>>> +
>>> +#undef N_STEPS
>>> +}
>>> +
>> ...
>>> +static void disable_boost(int fd)
>>> +{
>>> +     char *value;
>>> +
>>> +     value = igt_sysfs_get(fd, "gt_RPn_freq_mhz");
>>> +     igt_sysfs_set(fd, "gt_boost_freq_mhz", value);
>> Why is this needed? kernel will not clamp boost freq as well within
>> ctx_freq_min/max?
> Boosting is a separate mechanism than ctx->freq, as it is performed on
> behalf of *another* client.
Right. I meant i915 min|max_freq_context in your upcoming patch.
boost_freq is clamped against max_hw and min_user|soft|context
Understood that setting it to Rpn will make it get clamped in the 
expected range :)
Thanks for clarification.
>> Kernel disabling boost seems more effective than setting boost_freq to Rpn.
> This is how we tell the kernel to disable boost, by setting it to a
> value that never applies.
>
> The tests try to avoid triggering boosts, but I felt it was sensible to
> override the mechanism entirely. We still need various random sleeps
> inside the tests in order to give the worker a chance to run, which is a
> nuisance.
> -Chris

-- 
Thanks,
Sagar

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

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

* Re: [PATCH igt] igt: Add gem_ctx_freq to exercise requesting freq on a ctx
  2018-03-14  8:15   ` Sagar Arun Kamble
@ 2018-03-14  9:03     ` Chris Wilson
  2018-03-14  9:49       ` Sagar Arun Kamble
  0 siblings, 1 reply; 36+ messages in thread
From: Chris Wilson @ 2018-03-14  9:03 UTC (permalink / raw)
  To: Sagar Arun Kamble, intel-gfx; +Cc: igt-dev, Praveen Paneri

Quoting Sagar Arun Kamble (2018-03-14 08:15:15)
> 
> 
> On 3/13/2018 7:28 PM, Chris Wilson wrote:
> > Exercise some new API that allows applications to request that
> > individual contexts are executed within a desired frequency range.
> >
> > v2: Split single/continuous set_freq subtests
> > v3: Do an up/down ramp for individual freq request, check nothing
> > changes after each invalid request
> > v4: Check the frequencies reported by the kernel across the entire
> > range.
> > v5: Rewrite sandwich to create a sandwich between multiple concurrent
> > engines.
> > v6: Exercise sysfs overrides.
> > v7: Reset min/max of default context after independent(); don't ask
> > about failure
> > v8: Check transition beyond randomly chosen frequencies as well as
> > up/down ramps.
> >
> > Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> > Cc: Praveen Paneri <praveen.paneri@intel.com>
> > Cc: Sagar A Kamble <sagar.a.kamble@intel.com>
> > Cc: Antonio Argenziano <antonio.argenziano@intel.com>
> > Reviewed-by: Antonio Argenziano <antonio.argenziano@intel.com> #v5
> There are few stray whitespaces in __pmu_within_tolerance, pmu_assert.
> Otherwise looks good to me.
> Reviewed-by: Sagar Arun Kamble <sagar.a.kamble@intel.com>
> 
> Can you please clarify few things below:
> > ---
> <snip>
> > +
> > +static void sysfs_clamp(int fd, const struct intel_execution_engine *e)
> > +{
> > +#define N_STEPS 10
> > +     const unsigned int engine = e->exec_id | e->flags;
> > +     uint32_t ctx = gem_context_create(fd);
> > +     uint32_t sys_min, sys_max;
> > +     uint32_t min, max;
> > +     double measured;
> > +     igt_spin_t *spin;
> > +     int pmu;
> > +
> > +     get_sysfs_freq(&sys_min, &sys_max);
> > +     igt_info("System min freq: %dMHz; max freq: %dMHz\n", sys_min, sys_max);
> > +
> > +     get_freq(fd, ctx, &min, &max);
> > +     igt_info("Context min freq: %dMHz; max freq: %dMHz\n", min, max);
> > +
> > +     pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
> > +     igt_require(pmu >= 0);
> > +
> > +     for (int outer = 0; outer <= 2*N_STEPS; outer++) {
> > +             int ofrac = outer > N_STEPS ? 2*N_STEPS - outer : outer;
> > +             uint32_t ofreq = min + (max - min) * ofrac / N_STEPS;
> > +             uint32_t cur, discard;
> > +
> > +             for (int inner = 0; inner <= 2*N_STEPS; inner++) {
> > +                     int ifrac = inner > N_STEPS ? 2*N_STEPS - inner : inner;
> > +                     uint32_t ifreq = min + (max - min) * ifrac / N_STEPS;
> > +
> > +                     set_freq(fd, ctx, ifreq, ifreq);
> > +
> > +                     gem_quiescent_gpu(fd);
> > +                     spin = __igt_spin_batch_new(fd, ctx, engine, 0);
> > +                     usleep(10000);
> > +
> > +                     set_sysfs_freq(ofreq, ofreq);
> > +                     get_sysfs_freq(&cur, &discard);
> We don't sleep here because we know that we set the frequency in sysfs?

sysfs is a synchronous interface, yes.

> > +
> > +                     measured = measure_frequency(pmu, SAMPLE_PERIOD);
> > +                     igt_debugfs_dump(fd, "i915_rps_boost_info");
> > +
> > +                     set_sysfs_freq(sys_min, sys_max);
> > +
> > +                     igt_spin_batch_free(fd, spin);
> > +                     igt_info("%s(sysfs): Measured %.1fMHz, context %dMhz, expected %dMhz\n",
> > +                                     e->name, measured, ifreq, cur);
> > +                     pmu_assert(measured, cur);
> > +             }
> > +     }
> > +     gem_quiescent_gpu(fd);
> > +
> > +     close(pmu);
> > +     gem_context_destroy(fd, ctx);
> > +
> > +#undef N_STEPS
> > +}
> > +
> ...
> > +static void disable_boost(int fd)
> > +{
> > +     char *value;
> > +
> > +     value = igt_sysfs_get(fd, "gt_RPn_freq_mhz");
> > +     igt_sysfs_set(fd, "gt_boost_freq_mhz", value);
> Why is this needed? kernel will not clamp boost freq as well within 
> ctx_freq_min/max?

Boosting is a separate mechanism than ctx->freq, as it is performed on
behalf of *another* client.

> Kernel disabling boost seems more effective than setting boost_freq to Rpn.

This is how we tell the kernel to disable boost, by setting it to a
value that never applies.

The tests try to avoid triggering boosts, but I felt it was sensible to
override the mechanism entirely. We still need various random sleeps
inside the tests in order to give the worker a chance to run, which is a
nuisance.
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* Re: [PATCH igt] igt: Add gem_ctx_freq to exercise requesting freq on a ctx
  2018-03-13 13:58 ` Chris Wilson
@ 2018-03-14  8:15   ` Sagar Arun Kamble
  2018-03-14  9:03     ` Chris Wilson
  0 siblings, 1 reply; 36+ messages in thread
From: Sagar Arun Kamble @ 2018-03-14  8:15 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx; +Cc: igt-dev, Praveen Paneri



On 3/13/2018 7:28 PM, Chris Wilson wrote:
> Exercise some new API that allows applications to request that
> individual contexts are executed within a desired frequency range.
>
> v2: Split single/continuous set_freq subtests
> v3: Do an up/down ramp for individual freq request, check nothing
> changes after each invalid request
> v4: Check the frequencies reported by the kernel across the entire
> range.
> v5: Rewrite sandwich to create a sandwich between multiple concurrent
> engines.
> v6: Exercise sysfs overrides.
> v7: Reset min/max of default context after independent(); don't ask
> about failure
> v8: Check transition beyond randomly chosen frequencies as well as
> up/down ramps.
>
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> Cc: Praveen Paneri <praveen.paneri@intel.com>
> Cc: Sagar A Kamble <sagar.a.kamble@intel.com>
> Cc: Antonio Argenziano <antonio.argenziano@intel.com>
> Reviewed-by: Antonio Argenziano <antonio.argenziano@intel.com> #v5
There are few stray whitespaces in __pmu_within_tolerance, pmu_assert.
Otherwise looks good to me.
Reviewed-by: Sagar Arun Kamble <sagar.a.kamble@intel.com>

Can you please clarify few things below:
> ---
<snip>
> +
> +static void sysfs_clamp(int fd, const struct intel_execution_engine *e)
> +{
> +#define N_STEPS 10
> +	const unsigned int engine = e->exec_id | e->flags;
> +	uint32_t ctx = gem_context_create(fd);
> +	uint32_t sys_min, sys_max;
> +	uint32_t min, max;
> +	double measured;
> +	igt_spin_t *spin;
> +	int pmu;
> +
> +	get_sysfs_freq(&sys_min, &sys_max);
> +	igt_info("System min freq: %dMHz; max freq: %dMHz\n", sys_min, sys_max);
> +
> +	get_freq(fd, ctx, &min, &max);
> +	igt_info("Context min freq: %dMHz; max freq: %dMHz\n", min, max);
> +
> +	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
> +	igt_require(pmu >= 0);
> +
> +	for (int outer = 0; outer <= 2*N_STEPS; outer++) {
> +		int ofrac = outer > N_STEPS ? 2*N_STEPS - outer : outer;
> +		uint32_t ofreq = min + (max - min) * ofrac / N_STEPS;
> +		uint32_t cur, discard;
> +
> +		for (int inner = 0; inner <= 2*N_STEPS; inner++) {
> +			int ifrac = inner > N_STEPS ? 2*N_STEPS - inner : inner;
> +			uint32_t ifreq = min + (max - min) * ifrac / N_STEPS;
> +
> +			set_freq(fd, ctx, ifreq, ifreq);
> +
> +			gem_quiescent_gpu(fd);
> +			spin = __igt_spin_batch_new(fd, ctx, engine, 0);
> +			usleep(10000);
> +
> +			set_sysfs_freq(ofreq, ofreq);
> +			get_sysfs_freq(&cur, &discard);
We don't sleep here because we know that we set the frequency in sysfs?
> +
> +			measured = measure_frequency(pmu, SAMPLE_PERIOD);
> +			igt_debugfs_dump(fd, "i915_rps_boost_info");
> +
> +			set_sysfs_freq(sys_min, sys_max);
> +
> +			igt_spin_batch_free(fd, spin);
> +			igt_info("%s(sysfs): Measured %.1fMHz, context %dMhz, expected %dMhz\n",
> +					e->name, measured, ifreq, cur);
> +			pmu_assert(measured, cur);
> +		}
> +	}
> +	gem_quiescent_gpu(fd);
> +
> +	close(pmu);
> +	gem_context_destroy(fd, ctx);
> +
> +#undef N_STEPS
> +}
> +
...
> +static void disable_boost(int fd)
> +{
> +	char *value;
> +
> +	value = igt_sysfs_get(fd, "gt_RPn_freq_mhz");
> +	igt_sysfs_set(fd, "gt_boost_freq_mhz", value);
Why is this needed? kernel will not clamp boost freq as well within 
ctx_freq_min/max?
Kernel disabling boost seems more effective than setting boost_freq to Rpn.
> +	free(value);
> +}
> +
> +igt_main
> +{
> +	const struct intel_execution_engine *e;
> +	int fd = -1;
> +
> +	igt_fixture {
> +		fd = drm_open_driver(DRIVER_INTEL);
> +		igt_require_gem(fd);
> +
> +		igt_require(has_ctx_freq(fd));
> +
> +		sysfs = igt_sysfs_open(fd, NULL);
> +		igt_assert(sysfs != -1);
> +		igt_install_exit_handler(restore_sysfs_freq);
> +
> +		disable_boost(sysfs);
> +	}
> +
> +	igt_subtest("invalid")
> +		invalid(fd);
> +
> +	igt_subtest("idempotent")
> +		idempotent(fd);
> +
> +	igt_subtest("range")
> +		range(fd);
> +
> +	igt_subtest("independent")
> +		independent(fd);
> +
> +	igt_skip_on_simulation();
> +
> +	for (e = intel_execution_engines; e->name; e++) {
> +		igt_subtest_group {
> +			igt_fixture {
> +				gem_require_ring(fd, e->exec_id | e->flags);
> +			}
> +
> +			igt_subtest_f("%s-single", e->name)
> +				single(fd, e);
> +			igt_subtest_f("%s-continuous", e->name)
> +				continuous(fd, e);
> +			igt_subtest_f("%s-inflight", e->name)
> +				inflight(fd, e);
> +			igt_subtest_f("%s-sysfs", e->name)
> +				sysfs_clamp(fd, e);
> +		}
> +	}
> +
> +	igt_subtest("sandwich")
> +		sandwich(fd, 20);
> +
> +	igt_subtest("smoketest")
> +		smoketest(fd, 20);
> +}
> diff --git a/tests/meson.build b/tests/meson.build
> index 1176463c..f07ad25e 100644
> --- a/tests/meson.build
> +++ b/tests/meson.build
> @@ -36,6 +36,7 @@ test_progs = [
>   	'gem_ctx_create',
>   	'gem_ctx_exec',
>   	'gem_ctx_isolation',
> +	'gem_ctx_freq',
>   	'gem_ctx_param',
>   	'gem_ctx_switch',
>   	'gem_ctx_thrash',

-- 
Thanks,
Sagar

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

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

* [PATCH igt] igt: Add gem_ctx_freq to exercise requesting freq on a ctx
  2018-03-08 17:13 [PATCH igt] " Chris Wilson
                   ` (3 preceding siblings ...)
  2018-03-13 13:26 ` Chris Wilson
@ 2018-03-13 13:58 ` Chris Wilson
  2018-03-14  8:15   ` Sagar Arun Kamble
  4 siblings, 1 reply; 36+ messages in thread
From: Chris Wilson @ 2018-03-13 13:58 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev, Praveen Paneri

Exercise some new API that allows applications to request that
individual contexts are executed within a desired frequency range.

v2: Split single/continuous set_freq subtests
v3: Do an up/down ramp for individual freq request, check nothing
changes after each invalid request
v4: Check the frequencies reported by the kernel across the entire
range.
v5: Rewrite sandwich to create a sandwich between multiple concurrent
engines.
v6: Exercise sysfs overrides.
v7: Reset min/max of default context after independent(); don't ask
about failure
v8: Check transition beyond randomly chosen frequencies as well as
up/down ramps.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Praveen Paneri <praveen.paneri@intel.com>
Cc: Sagar A Kamble <sagar.a.kamble@intel.com>
Cc: Antonio Argenziano <antonio.argenziano@intel.com>
Reviewed-by: Antonio Argenziano <antonio.argenziano@intel.com> #v5
---
 tests/Makefile.am      |   1 +
 tests/Makefile.sources |   1 +
 tests/gem_ctx_freq.c   | 837 +++++++++++++++++++++++++++++++++++++++++++++++++
 tests/meson.build      |   1 +
 4 files changed, 840 insertions(+)
 create mode 100644 tests/gem_ctx_freq.c

diff --git a/tests/Makefile.am b/tests/Makefile.am
index dbc7be72..389f7fc7 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -104,6 +104,7 @@ drm_import_export_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 drm_import_export_LDADD = $(LDADD) -lpthread
 gem_close_race_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_close_race_LDADD = $(LDADD) -lpthread
+gem_ctx_freq_LDADD = $(LDADD) $(top_builddir)/lib/libigt_perf.la
 gem_ctx_thrash_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_ctx_thrash_LDADD = $(LDADD) -lpthread
 gem_exec_parallel_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index 4e6f5319..a4ca85bc 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -58,6 +58,7 @@ TESTS_progs = \
 	gem_ctx_bad_exec \
 	gem_ctx_create \
 	gem_ctx_exec \
+	gem_ctx_freq \
 	gem_ctx_isolation \
 	gem_ctx_param \
 	gem_ctx_switch \
diff --git a/tests/gem_ctx_freq.c b/tests/gem_ctx_freq.c
new file mode 100644
index 00000000..83509ea6
--- /dev/null
+++ b/tests/gem_ctx_freq.c
@@ -0,0 +1,837 @@
+/*
+ * 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 <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 <time.h>
+
+#include "igt.h"
+#include "igt_perf.h"
+#include "igt_sysfs.h"
+
+#define LOCAL_CONTEXT_PARAM_FREQUENCY 7
+
+#define SAMPLE_PERIOD (USEC_PER_SEC / 10)
+#define PMU_TOLERANCE 100
+
+static int sysfs = -1;
+
+static int __set_freq(int fd, uint32_t ctx, uint32_t min, uint32_t max)
+{
+	struct drm_i915_gem_context_param param = {
+		.ctx_id = ctx,
+		.param = LOCAL_CONTEXT_PARAM_FREQUENCY,
+		.value = (uint64_t)max << 32 | min,
+	};
+
+	return __gem_context_set_param(fd, &param);
+}
+
+static void set_freq(int fd, uint32_t ctx, uint32_t min, uint32_t max)
+{
+	igt_assert_eq(__set_freq(fd, ctx, min, max), 0);
+}
+
+static void get_freq(int fd, uint32_t ctx, uint32_t *min, uint32_t *max)
+{
+	struct drm_i915_gem_context_param param = {
+		.ctx_id = ctx,
+		.param = LOCAL_CONTEXT_PARAM_FREQUENCY,
+	};
+
+	gem_context_get_param(fd, &param);
+
+	*min = param.value & 0xffffffff;
+	*max = param.value >> 32;
+}
+
+static double measure_frequency(int pmu, int period_us)
+{
+	uint64_t data[2];
+	uint64_t d_t, d_v;
+
+	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 * 1e9 / d_t;
+}
+
+static bool __pmu_within_tolerance(double actual, double target)
+{
+	return (actual > target - PMU_TOLERANCE &&
+	       	actual < target + PMU_TOLERANCE);
+}
+
+static void pmu_assert(double actual, double target)
+{
+	igt_assert_f(__pmu_within_tolerance(actual, target),
+		     "Measured frequency %.2fMHz, is beyond target %.2f+-%dMhz", 
+		     actual, target, PMU_TOLERANCE);
+}
+
+static void single(int fd, const struct intel_execution_engine *e)
+{
+#define N_STEPS 10
+	const unsigned int engine = e->exec_id | e->flags;
+	uint32_t ctx = gem_context_create(fd);
+	uint32_t frequencies[2*N_STEPS + 1];
+	uint32_t min, max;
+	double measured;
+	igt_spin_t *spin;
+	int pmu;
+
+	get_freq(fd, ctx, &min, &max);
+	igt_info("Min freq: %dMHz; Max freq: %dMHz\n", min, max);
+
+	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
+	igt_require(pmu >= 0);
+
+	for (int step = 0; step <= 2*N_STEPS; step++) {
+		int frac = step > N_STEPS ? 2*N_STEPS - step : step;
+		frequencies[step] = min + (max - min) * frac / N_STEPS;
+	}
+
+	for (int pass = 0; pass < 3; pass++) {
+		for (int i = 0; i < ARRAY_SIZE(frequencies); i++) {
+			uint32_t freq = frequencies[i];
+			uint32_t cur, discard;
+
+			set_freq(fd, ctx, freq, freq);
+			get_freq(fd, ctx, &cur, &discard);
+
+			gem_quiescent_gpu(fd);
+			spin = __igt_spin_batch_new(fd, ctx, engine, 0);
+			usleep(10000);
+
+			measured = measure_frequency(pmu, SAMPLE_PERIOD);
+			igt_debugfs_dump(fd, "i915_rps_boost_info");
+
+			igt_spin_batch_free(fd, spin);
+			igt_info("%s(%s): Measured %.1fMHz, expected %dMhz\n",
+				 e->name, __func__,  measured, cur);
+			pmu_assert(measured, cur);
+		}
+
+		igt_permute_array(frequencies,
+				  ARRAY_SIZE(frequencies),
+				  igt_exchange_int);
+	}
+	gem_quiescent_gpu(fd);
+
+	close(pmu);
+	gem_context_destroy(fd, ctx);
+
+#undef N_STEPS
+}
+
+static void continuous(int fd, const struct intel_execution_engine *e)
+{
+#define N_STEPS 10
+	const unsigned int engine = e->exec_id | e->flags;
+	uint32_t ctx = gem_context_create(fd);
+	uint32_t frequencies[2*N_STEPS + 1];
+	uint32_t min, max;
+	double measured;
+	igt_spin_t *spin;
+	int pmu;
+
+	get_freq(fd, ctx, &min, &max);
+	igt_info("Min freq: %dMHz; Max freq: %dMHz\n", min, max);
+
+	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
+	igt_require(pmu >= 0);
+
+	for (int step = 0; step <= 2*N_STEPS; step++) {
+		int frac = step > N_STEPS ? 2*N_STEPS - step : step;
+		frequencies[step] = min + (max - min) * frac / N_STEPS;
+	}
+
+	gem_quiescent_gpu(fd);
+	spin = __igt_spin_batch_new(fd, ctx, engine, 0);
+	for (int pass = 0; pass < 3; pass++) {
+		for (int i = 0; i < ARRAY_SIZE(frequencies); i++) {
+			uint32_t freq = frequencies[i];
+			uint32_t cur, discard;
+			igt_spin_t *kick;
+
+			set_freq(fd, ctx, freq, freq);
+			get_freq(fd, ctx, &cur, &discard);
+
+			/*
+			 * When requesting a new frequency on the currently
+			 * executing context, it does not take effect until the
+			 * next context switch. In this case, we trigger a lite
+			 * restore.
+			 */
+			kick = __igt_spin_batch_new(fd, ctx, engine, 0);
+			igt_spin_batch_free(fd, spin);
+			spin = kick;
+
+			usleep(10000);
+
+			measured = measure_frequency(pmu, SAMPLE_PERIOD);
+			igt_debugfs_dump(fd, "i915_rps_boost_info");
+
+			igt_info("%s(continuous): Measured %.1fMHz, expected %dMhz\n",
+				 e->name, measured, cur);
+			pmu_assert(measured, cur);
+		}
+
+		igt_permute_array(frequencies,
+				  ARRAY_SIZE(frequencies),
+				  igt_exchange_int);
+	}
+	igt_spin_batch_free(fd, spin);
+	gem_quiescent_gpu(fd);
+
+	close(pmu);
+	gem_context_destroy(fd, ctx);
+#undef N_STEPS
+}
+
+static void inflight(int fd, const struct intel_execution_engine *e)
+{
+	const unsigned int engine = e->exec_id | e->flags;
+	uint32_t ctx, min, max, freq, discard;
+	double measured;
+	igt_spin_t *plug, *work[2];
+	int pmu;
+
+	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
+	igt_require(pmu >= 0);
+
+	ctx = gem_context_create(fd);
+	get_freq(fd, ctx, &min, &max);
+	set_freq(fd, ctx, min, min);
+
+	igt_info("Min freq: %dMHz; Max freq: %dMHz\n", min, max);
+
+	gem_quiescent_gpu(fd);
+	plug = igt_spin_batch_new(fd, ctx, engine, 0);
+	gem_context_destroy(fd, ctx);
+	for (int n = 0; n < 16; n++) {
+		struct drm_i915_gem_exec_object2 obj = {
+			.handle = plug->handle,
+		};
+		struct drm_i915_gem_execbuffer2 eb = {
+			.buffer_count = 1,
+			.buffers_ptr = to_user_pointer(&obj),
+			.flags = engine,
+			.rsvd1 = gem_context_create(fd),
+		};
+		set_freq(fd, eb.rsvd1, min, min);
+		gem_execbuf(fd, &eb);
+		gem_context_destroy(fd, eb.rsvd1);
+	}
+	measured = measure_frequency(pmu, SAMPLE_PERIOD);
+	igt_debugfs_dump(fd, "i915_rps_boost_info");
+	igt_info("%s(plug): Measured %.1fMHz, expected %dMhz\n",
+		 e->name, measured, min);
+	pmu_assert(measured, min);
+
+	ctx = gem_context_create(fd);
+	set_freq(fd, ctx, max, max);
+	work[0] = __igt_spin_batch_new(fd, ctx, engine, 0);
+
+	/* work is now queued but not executing */
+	freq = (max + min) / 2;
+	set_freq(fd, ctx, freq, freq);
+	get_freq(fd, ctx, &freq, &discard);
+	gem_context_destroy(fd, ctx);
+
+	ctx = gem_context_create(fd);
+	set_freq(fd, ctx, max, max);
+	work[1] = __igt_spin_batch_new(fd, ctx, engine, 0);
+	gem_context_destroy(fd, ctx);
+
+	igt_spin_batch_end(plug);
+	do
+		usleep(10000);
+	while (gem_bo_busy(fd, plug->handle));
+	igt_spin_batch_free(fd, plug);
+
+	/* Now work will execute */
+	measured = measure_frequency(pmu, SAMPLE_PERIOD);
+	igt_debugfs_dump(fd, "i915_engine_info");
+	igt_debugfs_dump(fd, "i915_rps_boost_info");
+	igt_info("%s(work0): Measured %.1fMHz, expected %dMhz\n",
+		 e->name, measured, freq);
+	pmu_assert(measured, freq);
+
+	igt_spin_batch_end(work[0]);
+	do
+		usleep(10000);
+	while (gem_bo_busy(fd, work[0]->handle));
+	igt_spin_batch_free(fd, work[0]);
+
+	measured = measure_frequency(pmu, SAMPLE_PERIOD);
+	igt_debugfs_dump(fd, "i915_engine_info");
+	igt_debugfs_dump(fd, "i915_rps_boost_info");
+	igt_info("%s(work1): Measured %.1fMHz, expected %dMhz\n",
+		 e->name, measured, max);
+	pmu_assert(measured, max);
+
+	igt_spin_batch_free(fd, work[1]);
+	close(pmu);
+	gem_quiescent_gpu(fd);
+}
+
+static void set_sysfs_freq(uint32_t min, uint32_t max)
+{
+	igt_sysfs_printf(sysfs, "gt_min_freq_mhz", "%u", min);
+	igt_sysfs_printf(sysfs, "gt_max_freq_mhz", "%u", max);
+}
+
+static void get_sysfs_freq(uint32_t *min, uint32_t *max)
+{
+	igt_sysfs_scanf(sysfs, "gt_min_freq_mhz", "%u", min);
+	igt_sysfs_scanf(sysfs, "gt_max_freq_mhz", "%u", max);
+}
+
+static void sysfs_clamp(int fd, const struct intel_execution_engine *e)
+{
+#define N_STEPS 10
+	const unsigned int engine = e->exec_id | e->flags;
+	uint32_t ctx = gem_context_create(fd);
+	uint32_t sys_min, sys_max;
+	uint32_t min, max;
+	double measured;
+	igt_spin_t *spin;
+	int pmu;
+
+	get_sysfs_freq(&sys_min, &sys_max);
+	igt_info("System min freq: %dMHz; max freq: %dMHz\n", sys_min, sys_max);
+
+	get_freq(fd, ctx, &min, &max);
+	igt_info("Context min freq: %dMHz; max freq: %dMHz\n", min, max);
+
+	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
+	igt_require(pmu >= 0);
+
+	for (int outer = 0; outer <= 2*N_STEPS; outer++) {
+		int ofrac = outer > N_STEPS ? 2*N_STEPS - outer : outer;
+		uint32_t ofreq = min + (max - min) * ofrac / N_STEPS;
+		uint32_t cur, discard;
+
+		for (int inner = 0; inner <= 2*N_STEPS; inner++) {
+			int ifrac = inner > N_STEPS ? 2*N_STEPS - inner : inner;
+			uint32_t ifreq = min + (max - min) * ifrac / N_STEPS;
+
+			set_freq(fd, ctx, ifreq, ifreq);
+
+			gem_quiescent_gpu(fd);
+			spin = __igt_spin_batch_new(fd, ctx, engine, 0);
+			usleep(10000);
+
+			set_sysfs_freq(ofreq, ofreq);
+			get_sysfs_freq(&cur, &discard);
+
+			measured = measure_frequency(pmu, SAMPLE_PERIOD);
+			igt_debugfs_dump(fd, "i915_rps_boost_info");
+
+			set_sysfs_freq(sys_min, sys_max);
+
+			igt_spin_batch_free(fd, spin);
+			igt_info("%s(sysfs): Measured %.1fMHz, context %dMhz, expected %dMhz\n",
+					e->name, measured, ifreq, cur);
+			pmu_assert(measured, cur);
+		}
+	}
+	gem_quiescent_gpu(fd);
+
+	close(pmu);
+	gem_context_destroy(fd, ctx);
+
+#undef N_STEPS
+}
+
+static void sandwich_engine(int fd, unsigned int engine, int timeout)
+{
+	uint32_t ctx = gem_context_create(fd);
+	uint32_t min, max;
+	int pmu;
+
+	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
+	igt_require(pmu >= 0);
+
+	get_freq(fd, ctx, &min, &max);
+
+	igt_until_timeout(timeout) {
+		uint32_t range[2];
+		igt_spin_t *spin;
+		double measured;
+
+		/* make sure we keep an overlap between all engines */
+		range[0] = min + (rand() % (max - min) / 2);
+		range[1] = max - (rand() % (max - min) / 2);
+
+		set_freq(fd, ctx, range[0], range[1]);
+		get_freq(fd, ctx, &range[0], &range[1]);
+
+		spin = __igt_spin_batch_new(fd, ctx, engine, 0);
+
+		usleep(10000);
+		measured = measure_frequency(pmu, SAMPLE_PERIOD);
+		igt_spin_batch_free(fd, spin);
+
+		igt_assert(measured >= range[0] - PMU_TOLERANCE &&
+			   measured <= range[1] + PMU_TOLERANCE);
+	}
+
+	gem_context_destroy(fd, ctx);
+	close(pmu);
+}
+
+static void sandwich(int fd, int timeout)
+{
+	unsigned int engine;
+
+	for_each_physical_engine(fd, engine) {
+		igt_fork(child, 1)
+			sandwich_engine(fd, engine, timeout);
+	}
+
+	igt_waitchildren();
+	gem_quiescent_gpu(fd);
+}
+
+static void pwm(int fd, unsigned int *engines, unsigned int nengine, int link)
+{
+	uint32_t ctx[nengine];
+
+	fcntl(link, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
+
+	for (unsigned int n = 0; n < nengine; n++)
+		ctx[n] = gem_context_create(fd);
+
+	do {
+		igt_spin_t *spin;
+		struct {
+			uint32_t engine;
+			uint32_t min;
+			uint32_t max;
+		} req;
+
+		while (read(link, &req, sizeof(req)) > 0) {
+			if ((req.engine | req.min | req.max) == 0)
+				goto out;
+
+			igt_assert(req.engine < nengine);
+			set_freq(fd, ctx[req.engine], req.min, req.max);
+		}
+
+		/* Create a 20% load using busy spinners */
+		spin = __igt_spin_batch_new(fd, ctx[0], engines[0], 0);
+		for (unsigned int n = 1; n < nengine; n++) {
+			struct drm_i915_gem_exec_object2 obj = {
+				.handle = spin->handle,
+			};
+			struct drm_i915_gem_execbuffer2 eb = {
+				.buffer_count = 1,
+				.buffers_ptr = to_user_pointer(&obj),
+				.flags = engines[n],
+				.rsvd1 = ctx[n],
+			};
+			gem_execbuf(fd, &eb);
+		}
+		usleep(100);
+		igt_spin_batch_end(spin);
+
+		do
+			usleep(10);
+		while (gem_bo_busy(fd, spin->handle));
+		igt_spin_batch_free(fd, spin);
+		usleep(400);
+	} while (1);
+
+out:
+	for (unsigned int n = 0; n < nengine; n++)
+		gem_context_destroy(fd, ctx[n]);
+}
+
+static void smoketest(int fd, int timeout)
+{
+	unsigned int engines[16];
+	unsigned int nengine;
+	unsigned int engine;
+	uint32_t min[16], max[16];
+	int pmu, link[2];
+
+	get_freq(fd, 0, &min[0], &max[0]);
+
+	nengine = 0;
+	for_each_physical_engine(fd, engine) {
+		if (nengine == ARRAY_SIZE(engines) - 1)
+			break;
+
+		min[nengine] = min[0];
+		max[nengine] = max[0];
+		engines[nengine] = engine;
+		nengine++;
+	}
+	igt_require(nengine);
+
+	igt_assert(pipe(link) == 0);
+	igt_fork(child, 1)
+		pwm(fd, engines, nengine, link[0]);
+	close(link[0]);
+
+	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
+	igt_require(pmu >= 0);
+
+	igt_until_timeout(timeout) {
+		struct {
+			uint32_t engine;
+			uint32_t min;
+			uint32_t max;
+		} req;
+		double measured;
+		uint32_t ctx;
+
+		req.engine = rand() % nengine;
+
+		ctx = gem_context_create(fd);
+		get_freq(fd, ctx, &req.min, &req.max);
+		req.min = rand() % (req.max - req.min) + req.min;
+		req.max = rand() % (req.max - req.min) + req.min;
+		set_freq(fd, ctx, req.min, req.max);
+		get_freq(fd, ctx, &req.min, &req.max);
+
+		igt_debug("Replacing (%d, %d) on engine %x with (%d, %d)\n",
+			  min[req.engine], max[req.engine], req.engine,
+			  req.min, req.max);
+		igt_assert(write(link[1], &req, sizeof(req)) == sizeof(req));
+		gem_context_destroy(fd, ctx);
+
+		min[req.engine] = req.min;
+		max[req.engine] = req.max;
+
+		for (unsigned int n = 0; n < nengine; n++) {
+			igt_debug("[%d]: [%d, %d]\n", n, min[n], max[n]);
+			if (min[n] < req.min)
+				req.min = min[n];
+			if (max[n] > req.max)
+				req.max = max[n];
+		}
+		igt_assert(req.max >= req.min);
+
+		usleep(50000);
+		measured = measure_frequency(pmu, SAMPLE_PERIOD);
+
+		if (measured <= req.min - PMU_TOLERANCE ||
+		    measured >= req.max + PMU_TOLERANCE)
+			igt_debugfs_dump(fd, "i915_rps_boost_info");
+		igt_info("Measured %.1fMHz, expected [%d, %d]Mhz\n",
+			 measured, req.min, req.max);
+		igt_assert(measured > req.min - PMU_TOLERANCE &&
+			   measured < req.max + PMU_TOLERANCE);
+	}
+
+	do {
+		struct {
+			uint32_t engine;
+			uint32_t min;
+			uint32_t max;
+		} req = {};
+
+		write(link[1], &req, sizeof(req));
+		close(link[1]);
+	} while (0);
+	igt_waitchildren();
+	gem_quiescent_gpu(fd);
+
+	close(pmu);
+}
+
+static void invalid_context(int fd, uint32_t ctx, uint32_t min, uint32_t max)
+{
+	const struct test {
+		uint32_t min, max;
+	} tests[] = {
+		{ min - 50, max - 50 },
+		{ min - 50, max },
+		{ min - 50, max + 50 },
+		{ min, max + 50 },
+		{ min + 50, max + 50 },
+
+		{ min - 50, min - 50 },
+
+		{ min - 50, min },
+		{ min + 50, min },
+		{ min, min - 50 },
+
+		{ max + 50, max },
+		{ max, max + 50 },
+		{ max, max - 50 },
+
+		{ max + 50, max + 50 },
+
+		{}
+	};
+
+	for (const struct test *t = tests; t->min | t->max; t++) {
+		uint32_t cur_min, cur_max;
+
+		igt_assert_f(__set_freq(fd, ctx, t->min, t->max) == -EINVAL,
+			     "Failed to reject invalid [%d, %d] (valid range [%d, %d]) on context %d\n",
+			     t->min, t->max, min, max, ctx);
+
+		get_freq(fd, 0, &cur_min, &cur_max);
+		igt_assert_eq(cur_min, min);
+		igt_assert_eq(cur_max, max);
+	}
+}
+
+static void invalid(int fd)
+{
+	uint32_t min, max, ctx;
+
+	get_freq(fd, 0, &min, &max);
+
+	invalid_context(fd, 0, min, max);
+
+	ctx = gem_context_create(fd);
+	invalid_context(fd, ctx, min, max);
+	gem_context_destroy(fd, ctx);
+}
+
+static void idempotent_context(int fd, uint32_t ctx)
+{
+	uint32_t min, max;
+	uint32_t cur_min, cur_max;
+
+	get_freq(fd, ctx, &min, &max);
+
+	set_freq(fd, ctx, max, max);
+	get_freq(fd, ctx, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, max);
+	igt_assert_eq(cur_max, max);
+
+	set_freq(fd, ctx, min, min);
+	get_freq(fd, ctx, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, min);
+
+	set_freq(fd, ctx, min, max);
+	get_freq(fd, ctx, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+}
+
+static void idempotent(int fd)
+{
+	uint32_t ctx;
+
+	idempotent_context(fd, 0);
+
+	ctx = gem_context_create(fd);
+	idempotent_context(fd, ctx);
+	gem_context_destroy(fd, ctx);
+}
+
+static void range_context(int fd, uint32_t ctx)
+{
+	uint32_t min, max;
+	uint32_t cur_min, cur_max;
+
+	get_freq(fd, ctx, &min, &max);
+
+	for (uint32_t freq = min; freq <= max; freq++) {
+		set_freq(fd, ctx, freq, freq);
+		get_freq(fd, ctx, &cur_min, &cur_max);
+
+		igt_assert(cur_min >= min);
+		igt_assert(cur_max <= max);
+	}
+
+	set_freq(fd, ctx, min, max);
+	get_freq(fd, ctx, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+}
+
+static void range(int fd)
+{
+	uint32_t ctx;
+
+	range_context(fd, 0);
+
+	ctx = gem_context_create(fd);
+	range_context(fd, ctx);
+	gem_context_destroy(fd, ctx);
+}
+
+static void independent(int fd)
+{
+	uint32_t min, max;
+	uint32_t cur_min, cur_max;
+	uint32_t ctx[2];
+
+	get_freq(fd, 0, &min, &max);
+
+	set_freq(fd, 0, max, max);
+	ctx[0] = gem_context_create(fd);
+	get_freq(fd, ctx[0], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+
+	set_freq(fd, 0, min, min);
+	get_freq(fd, ctx[0], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+
+	ctx[1] = gem_context_create(fd);
+	get_freq(fd, ctx[1], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+
+	set_freq(fd, ctx[1], max, max);
+	get_freq(fd, ctx[0], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+
+	get_freq(fd, 0, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, min);
+
+	get_freq(fd, ctx[1], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, max);
+	igt_assert_eq(cur_max, max);
+	gem_context_destroy(fd, ctx[1]);
+
+	get_freq(fd, ctx[0], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+	gem_context_destroy(fd, ctx[0]);
+
+	set_freq(fd, 0, min, max);
+	get_freq(fd, 0, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+}
+
+static bool has_ctx_freq(int fd)
+{
+	struct drm_i915_gem_context_param param = {
+		.param = LOCAL_CONTEXT_PARAM_FREQUENCY,
+	};
+
+	return __gem_context_get_param(fd, &param) == 0;
+}
+
+static void restore_sysfs_freq(int sig)
+{
+	char buf[256];
+
+	if (igt_sysfs_read(sysfs, "gt_RPn_freq_mhz", buf, sizeof(buf)) > 0) {
+		igt_sysfs_set(sysfs, "gt_idle_freq_mhz", buf);
+		igt_sysfs_set(sysfs, "gt_min_freq_mhz", buf);
+	}
+
+	if (igt_sysfs_read(sysfs, "gt_RP0_freq_mhz", buf, sizeof(buf)) > 0) {
+		igt_sysfs_set(sysfs, "gt_max_freq_mhz", buf);
+		igt_sysfs_set(sysfs, "gt_boost_freq_mhz", buf);
+	}
+}
+
+static void disable_boost(int fd)
+{
+	char *value;
+
+	value = igt_sysfs_get(fd, "gt_RPn_freq_mhz");
+	igt_sysfs_set(fd, "gt_boost_freq_mhz", value);
+	free(value);
+}
+
+igt_main
+{
+	const struct intel_execution_engine *e;
+	int fd = -1;
+
+	igt_fixture {
+		fd = drm_open_driver(DRIVER_INTEL);
+		igt_require_gem(fd);
+
+		igt_require(has_ctx_freq(fd));
+
+		sysfs = igt_sysfs_open(fd, NULL);
+		igt_assert(sysfs != -1);
+		igt_install_exit_handler(restore_sysfs_freq);
+
+		disable_boost(sysfs);
+	}
+
+	igt_subtest("invalid")
+		invalid(fd);
+
+	igt_subtest("idempotent")
+		idempotent(fd);
+
+	igt_subtest("range")
+		range(fd);
+
+	igt_subtest("independent")
+		independent(fd);
+
+	igt_skip_on_simulation();
+
+	for (e = intel_execution_engines; e->name; e++) {
+		igt_subtest_group {
+			igt_fixture {
+				gem_require_ring(fd, e->exec_id | e->flags);
+			}
+
+			igt_subtest_f("%s-single", e->name)
+				single(fd, e);
+			igt_subtest_f("%s-continuous", e->name)
+				continuous(fd, e);
+			igt_subtest_f("%s-inflight", e->name)
+				inflight(fd, e);
+			igt_subtest_f("%s-sysfs", e->name)
+				sysfs_clamp(fd, e);
+		}
+	}
+
+	igt_subtest("sandwich")
+		sandwich(fd, 20);
+
+	igt_subtest("smoketest")
+		smoketest(fd, 20);
+}
diff --git a/tests/meson.build b/tests/meson.build
index 1176463c..f07ad25e 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -36,6 +36,7 @@ test_progs = [
 	'gem_ctx_create',
 	'gem_ctx_exec',
 	'gem_ctx_isolation',
+	'gem_ctx_freq',
 	'gem_ctx_param',
 	'gem_ctx_switch',
 	'gem_ctx_thrash',
-- 
2.16.2

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

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

* [PATCH igt] igt: Add gem_ctx_freq to exercise requesting freq on a ctx
  2018-03-08 17:13 [PATCH igt] " Chris Wilson
                   ` (2 preceding siblings ...)
  2018-03-09 21:35 ` Chris Wilson
@ 2018-03-13 13:26 ` Chris Wilson
  2018-03-13 13:58 ` Chris Wilson
  4 siblings, 0 replies; 36+ messages in thread
From: Chris Wilson @ 2018-03-13 13:26 UTC (permalink / raw)
  To: intel-gfx; +Cc: igt-dev, Praveen Paneri

Exercise some new API that allows applications to request that
individual contexts are executed within a desired frequency range.

v2: Split single/continuous set_freq subtests
v3: Do an up/down ramp for individual freq request, check nothing
changes after each invalid request
v4: Check the frequencies reported by the kernel across the entire
range.
v5: Rewrite sandwich to create a sandwich between multiple concurrent
engines.
v6: Exercise sysfs overrides.
v7: Reset min/max of default context after independent(); don't ask
about failure

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Praveen Paneri <praveen.paneri@intel.com>
Cc: Sagar A Kamble <sagar.a.kamble@intel.com>
Cc: Antonio Argenziano <antonio.argenziano@intel.com>
Reviewed-by: Antonio Argenziano <antonio.argenziano@intel.com> #v5
---
 tests/Makefile.am      |   1 +
 tests/Makefile.sources |   1 +
 tests/gem_ctx_freq.c   | 800 +++++++++++++++++++++++++++++++++++++++++++++++++
 tests/meson.build      |   1 +
 4 files changed, 803 insertions(+)
 create mode 100644 tests/gem_ctx_freq.c

diff --git a/tests/Makefile.am b/tests/Makefile.am
index dbc7be72..389f7fc7 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -104,6 +104,7 @@ drm_import_export_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 drm_import_export_LDADD = $(LDADD) -lpthread
 gem_close_race_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_close_race_LDADD = $(LDADD) -lpthread
+gem_ctx_freq_LDADD = $(LDADD) $(top_builddir)/lib/libigt_perf.la
 gem_ctx_thrash_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_ctx_thrash_LDADD = $(LDADD) -lpthread
 gem_exec_parallel_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index 4e6f5319..a4ca85bc 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -58,6 +58,7 @@ TESTS_progs = \
 	gem_ctx_bad_exec \
 	gem_ctx_create \
 	gem_ctx_exec \
+	gem_ctx_freq \
 	gem_ctx_isolation \
 	gem_ctx_param \
 	gem_ctx_switch \
diff --git a/tests/gem_ctx_freq.c b/tests/gem_ctx_freq.c
new file mode 100644
index 00000000..35825ab8
--- /dev/null
+++ b/tests/gem_ctx_freq.c
@@ -0,0 +1,800 @@
+/*
+ * 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 <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 <time.h>
+
+#include "igt.h"
+#include "igt_perf.h"
+#include "igt_sysfs.h"
+
+#define LOCAL_CONTEXT_PARAM_FREQUENCY 7
+
+#define SAMPLE_PERIOD (USEC_PER_SEC / 10)
+
+static int sysfs = -1;
+
+static int __set_freq(int fd, uint32_t ctx, uint32_t min, uint32_t max)
+{
+	struct drm_i915_gem_context_param param = {
+		.ctx_id = ctx,
+		.param = LOCAL_CONTEXT_PARAM_FREQUENCY,
+		.value = (uint64_t)max << 32 | min,
+	};
+
+	return __gem_context_set_param(fd, &param);
+}
+
+static void set_freq(int fd, uint32_t ctx, uint32_t min, uint32_t max)
+{
+	igt_assert_eq(__set_freq(fd, ctx, min, max), 0);
+}
+
+static void get_freq(int fd, uint32_t ctx, uint32_t *min, uint32_t *max)
+{
+	struct drm_i915_gem_context_param param = {
+		.ctx_id = ctx,
+		.param = LOCAL_CONTEXT_PARAM_FREQUENCY,
+	};
+
+	gem_context_get_param(fd, &param);
+
+	*min = param.value & 0xffffffff;
+	*max = param.value >> 32;
+}
+
+static double measure_frequency(int pmu, int period_us)
+{
+	uint64_t data[2];
+	uint64_t d_t, d_v;
+
+	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 * 1e9 / d_t;
+}
+
+static void single(int fd, const struct intel_execution_engine *e)
+{
+#define N_STEPS 10
+	const unsigned int engine = e->exec_id | e->flags;
+	uint32_t ctx = gem_context_create(fd);
+	uint32_t min, max;
+	double measured;
+	igt_spin_t *spin;
+	int pmu;
+
+	get_freq(fd, ctx, &min, &max);
+	igt_info("Min freq: %dMHz; Max freq: %dMHz\n", min, max);
+
+	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
+	igt_require(pmu >= 0);
+
+	for (int step = 0; step <= 2*N_STEPS; step++) {
+		int frac = step > N_STEPS ? 2*N_STEPS - step : step;
+		uint32_t freq = min + (max - min) * frac / N_STEPS;
+		uint32_t cur, discard;
+
+		set_freq(fd, ctx, freq, freq);
+		get_freq(fd, ctx, &cur, &discard);
+
+		gem_quiescent_gpu(fd);
+		spin = __igt_spin_batch_new(fd, ctx, engine, 0);
+		usleep(10000);
+
+		measured = measure_frequency(pmu, SAMPLE_PERIOD);
+		igt_debugfs_dump(fd, "i915_rps_boost_info");
+
+		igt_spin_batch_free(fd, spin);
+		igt_info("%s(single): Measured %.1fMHz, expected %dMhz\n",
+			 e->name, measured, cur);
+		igt_assert(measured > cur - 100 && measured < cur + 100);
+	}
+	gem_quiescent_gpu(fd);
+
+	close(pmu);
+	gem_context_destroy(fd, ctx);
+
+#undef N_STEPS
+}
+
+static void continuous(int fd, const struct intel_execution_engine *e)
+{
+#define N_STEPS 10
+	const unsigned int engine = e->exec_id | e->flags;
+	uint32_t ctx = gem_context_create(fd);
+	uint32_t min, max;
+	double measured;
+	igt_spin_t *spin;
+	int pmu;
+
+	get_freq(fd, ctx, &min, &max);
+	igt_info("Min freq: %dMHz; Max freq: %dMHz\n", min, max);
+
+	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
+	igt_require(pmu >= 0);
+
+	gem_quiescent_gpu(fd);
+	spin = __igt_spin_batch_new(fd, ctx, engine, 0);
+	for (int step = 0; step <= 2*N_STEPS; step++) {
+		int frac = step > N_STEPS ? 2*N_STEPS - step : step;
+		uint32_t freq = min + (max - min) * frac / N_STEPS;
+		uint32_t cur, discard;
+		igt_spin_t *kick;
+
+		set_freq(fd, ctx, freq, freq);
+		get_freq(fd, ctx, &cur, &discard);
+
+		/*
+		 * When requesting a new frequency on the currently
+		 * executing context, it does not take effect until the
+		 * next context switch. In this case, we trigger a lite
+		 * restore.
+		 */
+		kick = __igt_spin_batch_new(fd, ctx, engine, 0);
+		igt_spin_batch_free(fd, spin);
+		spin = kick;
+
+		usleep(10000);
+
+		measured = measure_frequency(pmu, SAMPLE_PERIOD);
+		igt_debugfs_dump(fd, "i915_rps_boost_info");
+
+		igt_info("%s(continuous): Measured %.1fMHz, expected %dMhz\n",
+			 e->name, measured, cur);
+		igt_assert(measured > cur - 100 && measured < cur + 100);
+	}
+	igt_spin_batch_free(fd, spin);
+	gem_quiescent_gpu(fd);
+
+	close(pmu);
+	gem_context_destroy(fd, ctx);
+#undef N_STEPS
+}
+
+static void inflight(int fd, const struct intel_execution_engine *e)
+{
+	const unsigned int engine = e->exec_id | e->flags;
+	uint32_t ctx, min, max, freq, discard;
+	double measured;
+	igt_spin_t *plug, *work[2];
+	int pmu;
+
+	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
+	igt_require(pmu >= 0);
+
+	ctx = gem_context_create(fd);
+	get_freq(fd, ctx, &min, &max);
+	set_freq(fd, ctx, min, min);
+
+	igt_info("Min freq: %dMHz; Max freq: %dMHz\n", min, max);
+
+	gem_quiescent_gpu(fd);
+	plug = igt_spin_batch_new(fd, ctx, engine, 0);
+	gem_context_destroy(fd, ctx);
+	for (int n = 0; n < 16; n++) {
+		struct drm_i915_gem_exec_object2 obj = {
+			.handle = plug->handle,
+		};
+		struct drm_i915_gem_execbuffer2 eb = {
+			.buffer_count = 1,
+			.buffers_ptr = to_user_pointer(&obj),
+			.flags = engine,
+			.rsvd1 = gem_context_create(fd),
+		};
+		set_freq(fd, eb.rsvd1, min, min);
+		gem_execbuf(fd, &eb);
+		gem_context_destroy(fd, eb.rsvd1);
+	}
+	measured = measure_frequency(pmu, SAMPLE_PERIOD);
+	igt_debugfs_dump(fd, "i915_rps_boost_info");
+	igt_info("%s(plug): Measured %.1fMHz, expected %dMhz\n",
+		 e->name, measured, min);
+	igt_assert(measured > min - 100 && measured < min + 100);
+
+	ctx = gem_context_create(fd);
+	set_freq(fd, ctx, max, max);
+	work[0] = __igt_spin_batch_new(fd, ctx, engine, 0);
+
+	/* work is now queued but not executing */
+	freq = (max + min) / 2;
+	set_freq(fd, ctx, freq, freq);
+	get_freq(fd, ctx, &freq, &discard);
+	gem_context_destroy(fd, ctx);
+
+	ctx = gem_context_create(fd);
+	set_freq(fd, ctx, max, max);
+	work[1] = __igt_spin_batch_new(fd, ctx, engine, 0);
+	gem_context_destroy(fd, ctx);
+
+	igt_spin_batch_end(plug);
+	do
+		usleep(10000);
+	while (gem_bo_busy(fd, plug->handle));
+	igt_spin_batch_free(fd, plug);
+
+	/* Now work will execute */
+	measured = measure_frequency(pmu, SAMPLE_PERIOD);
+	igt_debugfs_dump(fd, "i915_engine_info");
+	igt_debugfs_dump(fd, "i915_rps_boost_info");
+	igt_info("%s(work0): Measured %.1fMHz, expected %dMhz\n",
+		 e->name, measured, freq);
+	igt_assert(measured > freq - 100 && measured < freq + 100);
+
+	igt_spin_batch_end(work[0]);
+	do
+		usleep(10000);
+	while (gem_bo_busy(fd, work[0]->handle));
+	igt_spin_batch_free(fd, work[0]);
+
+	measured = measure_frequency(pmu, SAMPLE_PERIOD);
+	igt_debugfs_dump(fd, "i915_engine_info");
+	igt_debugfs_dump(fd, "i915_rps_boost_info");
+	igt_info("%s(work1): Measured %.1fMHz, expected %dMhz\n",
+		 e->name, measured, max);
+	igt_assert(measured > max - 100 && measured < max + 100);
+
+	igt_spin_batch_free(fd, work[1]);
+	close(pmu);
+	gem_quiescent_gpu(fd);
+}
+
+static void set_sysfs_freq(uint32_t min, uint32_t max)
+{
+	igt_sysfs_printf(sysfs, "gt_min_freq_mhz", "%u", min);
+	igt_sysfs_printf(sysfs, "gt_max_freq_mhz", "%u", max);
+}
+
+static void get_sysfs_freq(uint32_t *min, uint32_t *max)
+{
+	igt_sysfs_scanf(sysfs, "gt_min_freq_mhz", "%u", min);
+	igt_sysfs_scanf(sysfs, "gt_max_freq_mhz", "%u", max);
+}
+
+static void sysfs_clamp(int fd, const struct intel_execution_engine *e)
+{
+#define N_STEPS 10
+	const unsigned int engine = e->exec_id | e->flags;
+	uint32_t ctx = gem_context_create(fd);
+	uint32_t sys_min, sys_max;
+	uint32_t min, max;
+	double measured;
+	igt_spin_t *spin;
+	int pmu;
+
+	get_sysfs_freq(&sys_min, &sys_max);
+	igt_info("System min freq: %dMHz; max freq: %dMHz\n", sys_min, sys_max);
+
+	get_freq(fd, ctx, &min, &max);
+	igt_info("Context min freq: %dMHz; max freq: %dMHz\n", min, max);
+
+	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
+	igt_require(pmu >= 0);
+
+	for (int outer = 0; outer <= 2*N_STEPS; outer++) {
+		int ofrac = outer > N_STEPS ? 2*N_STEPS - outer : outer;
+		uint32_t ofreq = min + (max - min) * ofrac / N_STEPS;
+		uint32_t cur, discard;
+
+		for (int inner = 0; inner <= 2*N_STEPS; inner++) {
+			int ifrac = inner > N_STEPS ? 2*N_STEPS - inner : inner;
+			uint32_t ifreq = min + (max - min) * ifrac / N_STEPS;
+
+			set_freq(fd, ctx, ifreq, ifreq);
+
+			gem_quiescent_gpu(fd);
+			spin = __igt_spin_batch_new(fd, ctx, engine, 0);
+			usleep(10000);
+
+			set_sysfs_freq(ofreq, ofreq);
+			get_sysfs_freq(&cur, &discard);
+
+			measured = measure_frequency(pmu, SAMPLE_PERIOD);
+			igt_debugfs_dump(fd, "i915_rps_boost_info");
+
+			set_sysfs_freq(sys_min, sys_max);
+
+			igt_spin_batch_free(fd, spin);
+			igt_info("%s(sysfs): Measured %.1fMHz, context %dMhz, expected %dMhz\n",
+					e->name, measured, ifreq, cur);
+			igt_assert(measured > cur - 100 && measured < cur + 100);
+		}
+	}
+	gem_quiescent_gpu(fd);
+
+	close(pmu);
+	gem_context_destroy(fd, ctx);
+
+#undef N_STEPS
+}
+
+static void sandwich_engine(int fd, unsigned int engine, int timeout)
+{
+	uint32_t ctx = gem_context_create(fd);
+	uint32_t min, max;
+	int pmu;
+
+	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
+	igt_require(pmu >= 0);
+
+	get_freq(fd, ctx, &min, &max);
+
+	igt_until_timeout(timeout) {
+		uint32_t range[2];
+		igt_spin_t *spin;
+		double measured;
+
+		/* make sure we keep an overlap between all engines */
+		range[0] = min + (rand() % (max - min) / 2);
+		range[1] = max - (rand() % (max - min) / 2);
+
+		set_freq(fd, ctx, range[0], range[1]);
+		get_freq(fd, ctx, &range[0], &range[1]);
+
+		spin = __igt_spin_batch_new(fd, ctx, engine, 0);
+
+		usleep(10000);
+		measured = measure_frequency(pmu, SAMPLE_PERIOD);
+		igt_spin_batch_free(fd, spin);
+
+		igt_assert(measured >= range[0] - 100 &&
+			   measured <= range[1] + 100);
+	}
+
+	gem_context_destroy(fd, ctx);
+	close(pmu);
+}
+
+static void sandwich(int fd, int timeout)
+{
+	unsigned int engine;
+
+	for_each_physical_engine(fd, engine) {
+		igt_fork(child, 1)
+			sandwich_engine(fd, engine, timeout);
+	}
+
+	igt_waitchildren();
+	gem_quiescent_gpu(fd);
+}
+
+static void pwm(int fd, unsigned int *engines, unsigned int nengine, int link)
+{
+	uint32_t ctx[nengine];
+
+	fcntl(link, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
+
+	for (unsigned int n = 0; n < nengine; n++)
+		ctx[n] = gem_context_create(fd);
+
+	do {
+		igt_spin_t *spin;
+		struct {
+			uint32_t engine;
+			uint32_t min;
+			uint32_t max;
+		} req;
+
+		while (read(link, &req, sizeof(req)) > 0) {
+			if ((req.engine | req.min | req.max) == 0)
+				goto out;
+
+			igt_assert(req.engine < nengine);
+			set_freq(fd, ctx[req.engine], req.min, req.max);
+		}
+
+		/* Create a 20% load using busy spinners */
+		spin = __igt_spin_batch_new(fd, ctx[0], engines[0], 0);
+		for (unsigned int n = 1; n < nengine; n++) {
+			struct drm_i915_gem_exec_object2 obj = {
+				.handle = spin->handle,
+			};
+			struct drm_i915_gem_execbuffer2 eb = {
+				.buffer_count = 1,
+				.buffers_ptr = to_user_pointer(&obj),
+				.flags = engines[n],
+				.rsvd1 = ctx[n],
+			};
+			gem_execbuf(fd, &eb);
+		}
+		usleep(100);
+		igt_spin_batch_end(spin);
+
+		do
+			usleep(10);
+		while (gem_bo_busy(fd, spin->handle));
+		igt_spin_batch_free(fd, spin);
+		usleep(400);
+	} while (1);
+
+out:
+	for (unsigned int n = 0; n < nengine; n++)
+		gem_context_destroy(fd, ctx[n]);
+}
+
+static void smoketest(int fd, int timeout)
+{
+	unsigned int engines[16];
+	unsigned int nengine;
+	unsigned int engine;
+	uint32_t min[16], max[16];
+	int pmu, link[2];
+
+	get_freq(fd, 0, &min[0], &max[0]);
+
+	nengine = 0;
+	for_each_physical_engine(fd, engine) {
+		if (nengine == ARRAY_SIZE(engines) - 1)
+			break;
+
+		min[nengine] = min[0];
+		max[nengine] = max[0];
+		engines[nengine] = engine;
+		nengine++;
+	}
+	igt_require(nengine);
+
+	igt_assert(pipe(link) == 0);
+	igt_fork(child, 1)
+		pwm(fd, engines, nengine, link[0]);
+	close(link[0]);
+
+	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
+	igt_require(pmu >= 0);
+
+	igt_until_timeout(timeout) {
+		struct {
+			uint32_t engine;
+			uint32_t min;
+			uint32_t max;
+		} req;
+		double measured;
+		uint32_t ctx;
+
+		req.engine = rand() % nengine;
+
+		ctx = gem_context_create(fd);
+		get_freq(fd, ctx, &req.min, &req.max);
+		req.min = rand() % (req.max - req.min) + req.min;
+		req.max = rand() % (req.max - req.min) + req.min;
+		set_freq(fd, ctx, req.min, req.max);
+		get_freq(fd, ctx, &req.min, &req.max);
+
+		igt_debug("Replacing (%d, %d) on engine %x with (%d, %d)\n",
+			  min[req.engine], max[req.engine], req.engine,
+			  req.min, req.max);
+		igt_assert(write(link[1], &req, sizeof(req)) == sizeof(req));
+		gem_context_destroy(fd, ctx);
+
+		min[req.engine] = req.min;
+		max[req.engine] = req.max;
+
+		for (unsigned int n = 0; n < nengine; n++) {
+			igt_debug("[%d]: [%d, %d]\n", n, min[n], max[n]);
+			if (min[n] < req.min)
+				req.min = min[n];
+			if (max[n] > req.max)
+				req.max = max[n];
+		}
+		igt_assert(req.max >= req.min);
+
+		usleep(50000);
+		measured = measure_frequency(pmu, SAMPLE_PERIOD);
+
+		if (measured <= req.min - 100 || measured >= req.max + 100)
+			igt_debugfs_dump(fd, "i915_rps_boost_info");
+		igt_info("Measured %.1fMHz, expected [%d, %d]Mhz\n",
+			 measured, req.min, req.max);
+		igt_assert(measured > req.min - 100 &&
+			   measured < req.max + 100);
+	}
+
+	do {
+		struct {
+			uint32_t engine;
+			uint32_t min;
+			uint32_t max;
+		} req = {};
+
+		write(link[1], &req, sizeof(req));
+		close(link[1]);
+	} while (0);
+	igt_waitchildren();
+	gem_quiescent_gpu(fd);
+
+	close(pmu);
+}
+
+static void invalid_context(int fd, uint32_t ctx, uint32_t min, uint32_t max)
+{
+	const struct test {
+		uint32_t min, max;
+	} tests[] = {
+		{ min - 50, max - 50 },
+		{ min - 50, max },
+		{ min - 50, max + 50 },
+		{ min, max + 50 },
+		{ min + 50, max + 50 },
+
+		{ min - 50, min - 50 },
+
+		{ min - 50, min },
+		{ min + 50, min },
+		{ min, min - 50 },
+
+		{ max + 50, max },
+		{ max, max + 50 },
+		{ max, max - 50 },
+
+		{ max + 50, max + 50 },
+
+		{}
+	};
+
+	for (const struct test *t = tests; t->min | t->max; t++) {
+		uint32_t cur_min, cur_max;
+
+		igt_assert_f(__set_freq(fd, ctx, t->min, t->max) == -EINVAL,
+			     "Failed to reject invalid [%d, %d] (valid range [%d, %d]) on context %d\n",
+			     t->min, t->max, min, max, ctx);
+
+		get_freq(fd, 0, &cur_min, &cur_max);
+		igt_assert_eq(cur_min, min);
+		igt_assert_eq(cur_max, max);
+	}
+}
+
+static void invalid(int fd)
+{
+	uint32_t min, max, ctx;
+
+	get_freq(fd, 0, &min, &max);
+
+	invalid_context(fd, 0, min, max);
+
+	ctx = gem_context_create(fd);
+	invalid_context(fd, ctx, min, max);
+	gem_context_destroy(fd, ctx);
+}
+
+static void idempotent_context(int fd, uint32_t ctx)
+{
+	uint32_t min, max;
+	uint32_t cur_min, cur_max;
+
+	get_freq(fd, ctx, &min, &max);
+
+	set_freq(fd, ctx, max, max);
+	get_freq(fd, ctx, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, max);
+	igt_assert_eq(cur_max, max);
+
+	set_freq(fd, ctx, min, min);
+	get_freq(fd, ctx, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, min);
+
+	set_freq(fd, ctx, min, max);
+	get_freq(fd, ctx, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+}
+
+static void idempotent(int fd)
+{
+	uint32_t ctx;
+
+	idempotent_context(fd, 0);
+
+	ctx = gem_context_create(fd);
+	idempotent_context(fd, ctx);
+	gem_context_destroy(fd, ctx);
+}
+
+static void range_context(int fd, uint32_t ctx)
+{
+	uint32_t min, max;
+	uint32_t cur_min, cur_max;
+
+	get_freq(fd, ctx, &min, &max);
+
+	for (uint32_t freq = min; freq <= max; freq++) {
+		set_freq(fd, ctx, freq, freq);
+		get_freq(fd, ctx, &cur_min, &cur_max);
+
+		igt_assert(cur_min >= min);
+		igt_assert(cur_max <= max);
+	}
+
+	set_freq(fd, ctx, min, max);
+	get_freq(fd, ctx, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+}
+
+static void range(int fd)
+{
+	uint32_t ctx;
+
+	range_context(fd, 0);
+
+	ctx = gem_context_create(fd);
+	range_context(fd, ctx);
+	gem_context_destroy(fd, ctx);
+}
+
+static void independent(int fd)
+{
+	uint32_t min, max;
+	uint32_t cur_min, cur_max;
+	uint32_t ctx[2];
+
+	get_freq(fd, 0, &min, &max);
+
+	set_freq(fd, 0, max, max);
+	ctx[0] = gem_context_create(fd);
+	get_freq(fd, ctx[0], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+
+	set_freq(fd, 0, min, min);
+	get_freq(fd, ctx[0], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+
+	ctx[1] = gem_context_create(fd);
+	get_freq(fd, ctx[1], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+
+	set_freq(fd, ctx[1], max, max);
+	get_freq(fd, ctx[0], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+
+	get_freq(fd, 0, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, min);
+
+	get_freq(fd, ctx[1], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, max);
+	igt_assert_eq(cur_max, max);
+	gem_context_destroy(fd, ctx[1]);
+
+	get_freq(fd, ctx[0], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+	gem_context_destroy(fd, ctx[0]);
+
+	set_freq(fd, 0, &min, &max);
+	get_freq(fd, 0, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, min);
+}
+
+static bool has_ctx_freq(int fd)
+{
+	struct drm_i915_gem_context_param param = {
+		.param = LOCAL_CONTEXT_PARAM_FREQUENCY,
+	};
+
+	return __gem_context_get_param(fd, &param) == 0;
+}
+
+static void restore_sysfs_freq(int sig)
+{
+	char buf[256];
+
+	if (igt_sysfs_read(sysfs, "gt_RPn_freq_mhz", buf, sizeof(buf)) > 0) {
+		igt_sysfs_set(sysfs, "gt_idle_freq_mhz", buf);
+		igt_sysfs_set(sysfs, "gt_min_freq_mhz", buf);
+	}
+
+	if (igt_sysfs_read(sysfs, "gt_RP0_freq_mhz", buf, sizeof(buf)) > 0) {
+		igt_sysfs_set(sysfs, "gt_max_freq_mhz", buf);
+		igt_sysfs_set(sysfs, "gt_boost_freq_mhz", buf);
+	}
+}
+
+static void disable_boost(int fd)
+{
+	char *value;
+
+	value = igt_sysfs_get(fd, "gt_RPn_freq_mhz");
+	igt_sysfs_set(fd, "gt_boost_freq_mhz", value);
+	free(value);
+}
+
+igt_main
+{
+	const struct intel_execution_engine *e;
+	int fd = -1;
+
+	igt_fixture {
+		fd = drm_open_driver(DRIVER_INTEL);
+		igt_require_gem(fd);
+
+		igt_require(has_ctx_freq(fd));
+
+		sysfs = igt_sysfs_open(fd, NULL);
+		igt_assert(sysfs != -1);
+		igt_install_exit_handler(restore_sysfs_freq);
+
+		disable_boost(sysfs);
+	}
+
+	igt_subtest("invalid")
+		invalid(fd);
+
+	igt_subtest("idempotent")
+		idempotent(fd);
+
+	igt_subtest("range")
+		range(fd);
+
+	igt_subtest("independent")
+		independent(fd);
+
+	igt_skip_on_simulation();
+
+	for (e = intel_execution_engines; e->name; e++) {
+		igt_subtest_group {
+			igt_fixture {
+				gem_require_ring(fd, e->exec_id | e->flags);
+			}
+
+			igt_subtest_f("%s-single", e->name)
+				single(fd, e);
+			igt_subtest_f("%s-continuous", e->name)
+				continuous(fd, e);
+			igt_subtest_f("%s-inflight", e->name)
+				inflight(fd, e);
+			igt_subtest_f("%s-sysfs", e->name)
+				sysfs_clamp(fd, e);
+		}
+	}
+
+	igt_subtest("sandwich")
+		sandwich(fd, 20);
+
+	igt_subtest("smoketest")
+		smoketest(fd, 20);
+}
diff --git a/tests/meson.build b/tests/meson.build
index 1176463c..f07ad25e 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -36,6 +36,7 @@ test_progs = [
 	'gem_ctx_create',
 	'gem_ctx_exec',
 	'gem_ctx_isolation',
+	'gem_ctx_freq',
 	'gem_ctx_param',
 	'gem_ctx_switch',
 	'gem_ctx_thrash',
-- 
2.16.2

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

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

* Re: [PATCH igt] igt: Add gem_ctx_freq to exercise requesting freq on a ctx
  2018-03-13 12:38   ` Sagar Arun Kamble
@ 2018-03-13 12:50     ` Chris Wilson
  0 siblings, 0 replies; 36+ messages in thread
From: Chris Wilson @ 2018-03-13 12:50 UTC (permalink / raw)
  To: Sagar Arun Kamble, intel-gfx; +Cc: Praveen Paneri

Quoting Sagar Arun Kamble (2018-03-13 12:38:04)
> 
> 
> On 3/10/2018 3:05 AM, Chris Wilson wrote:
> > +static void single(int fd, const struct intel_execution_engine *e)
> > +{
> > +#define N_STEPS 10
> > +     const unsigned int engine = e->exec_id | e->flags;
> > +     uint32_t ctx = gem_context_create(fd);
> > +     uint32_t min, max;
> > +     double measured;
> > +     igt_spin_t *spin;
> > +     int pmu;
> > +
> > +     get_freq(fd, ctx, &min, &max);
> > +     igt_info("Min freq: %dMHz; Max freq: %dMHz\n", min, max);
> > +
> > +     pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
> > +     igt_require(pmu >= 0);
> This igt_require can go to igt_fixture below.

Why?

> > +
> > +     for (int step = 0; step <= 2*N_STEPS; step++) {
> > +             int frac = step > N_STEPS ? 2*N_STEPS - step : step;
> > +             uint32_t freq = min + (max - min) * frac / N_STEPS;
> > +             uint32_t cur, discard;
> > +
> > +             set_freq(fd, ctx, freq, freq);
> > +             get_freq(fd, ctx, &cur, &discard);
> > +
> > +             gem_quiescent_gpu(fd);
> > +             spin = __igt_spin_batch_new(fd, ctx, engine, 0);
> > +             usleep(10000);
> > +
> > +             measured = measure_frequency(pmu, SAMPLE_PERIOD);
> > +             igt_debugfs_dump(fd, "i915_rps_boost_info");
> > +
> > +             igt_spin_batch_free(fd, spin);
> > +             igt_info("%s(single): Measured %.1fMHz, expected %dMhz\n",
> > +                      e->name, measured, cur);
> > +             igt_assert(measured > cur - 100 && measured < cur + 100);
> Is this margin of 100Mhz for PMU accuracy?

Yes, even then it sometimes exceeds it.

> > +     }
> > +     gem_quiescent_gpu(fd);
> > +
> > +     close(pmu);
> > +     gem_context_destroy(fd, ctx);
> > +
> > +#undef N_STEPS
> > +}

> > +static void inflight(int fd, const struct intel_execution_engine *e)
> > +{
...
> > +     measured = measure_frequency(pmu, SAMPLE_PERIOD);
> > +     igt_debugfs_dump(fd, "i915_rps_boost_info");
> > +     igt_info("%s(plug): Measured %.1fMHz, expected %dMhz\n",
> > +              e->name, measured, min);
> > +     igt_assert(measured > min - 100 && measured < min + 100);
> > +
> > +     ctx = gem_context_create(fd);
> > +     set_freq(fd, ctx, max, max);
> this set_freq can be removed.

No, we want to check it obeys the later restriction and not this one.

> > +     work[0] = __igt_spin_batch_new(fd, ctx, engine, 0);
> > +
> > +     /* work is now queued but not executing */
> > +     freq = (max + min) / 2;
> > +     set_freq(fd, ctx, freq, freq);
> > +     get_freq(fd, ctx, &freq, &discard);
> > +     gem_context_destroy(fd, ctx);

> > +             for (unsigned int n = 0; n < nengine; n++) {
> > +                     igt_debug("[%d]: [%d, %d]\n", n, min[n], max[n]);
> > +                     if (min[n] < req.min)
> > +                             req.min = min[n];
> > +                     if (max[n] > req.max)
> > +                             req.max = max[n];
> > +             }
> I thought policy i915 will be implementing is max of mins and min of maxes.

But the only policy I want userspace to be aware of is that the actual
frequency will be inside their desired range. (Unless contradicted by
the system). That's the ABI we want; how we handle it internally should
be left open with the prospect of either changing it or making it
adjustable.

> > +static void invalid_context(int fd, uint32_t ctx, uint32_t min, uint32_t max)
> > +{
> > +     const struct test {
> > +             uint32_t min, max;
> > +     } tests[] = {
> > +             { min - 50, max - 50 },
> > +             { min - 50, max },
> > +             { min - 50, max + 50 },
> > +             { min, max + 50 },
> > +             { min + 50, max + 50 },
> > +
> > +             { min - 50, min - 50 },
> > +
> > +             { min - 50, min },
> This one is similar to { min -  50, max } where max is in range but min 
> is outside.
> Similarly on max side. what is the reasoning for these cases?

Checking two wrongs don't make a right.

> > +             { min + 50, min },
> > +             { min, min - 50 },
> > +
> > +             { max + 50, max },
> > +             { max, max + 50 },
> > +             { max, max - 50 },
> > +
> > +             { max + 50, max + 50 },
> > +
> > +             {}
> > +     };
> > +
> > +     for (const struct test *t = tests; t->min | t->max; t++) {
> > +             uint32_t cur_min, cur_max;
> > +
> > +             igt_assert_f(__set_freq(fd, ctx, t->min, t->max) == -EINVAL,
> > +                          "Failed to reject invalid [%d, %d] (valid range [%d, %d]) on context %d\n",
> > +                          t->min, t->max, min, max, ctx);
> > +
> > +             get_freq(fd, 0, &cur_min, &cur_max);
> > +             igt_assert_eq(cur_min, min);
> > +             igt_assert_eq(cur_max, max);
> > +     }
> > +}
> > +
> > +static void independent(int fd)
> > +{
> > +     uint32_t min, max;
> > +     uint32_t cur_min, cur_max;
> > +     uint32_t ctx[2];
> > +
> > +     get_freq(fd, 0, &min, &max);
> > +
> > +     set_freq(fd, 0, max, max);
> > +     ctx[0] = gem_context_create(fd);
> > +     get_freq(fd, ctx[0], &cur_min, &cur_max);
> > +     igt_assert_eq(cur_min, min);
> > +     igt_assert_eq(cur_max, max);
> > +
> > +     set_freq(fd, 0, min, min);
> > +     get_freq(fd, ctx[0], &cur_min, &cur_max);
> > +     igt_assert_eq(cur_min, min);
> > +     igt_assert_eq(cur_max, max);
> > +
> > +     ctx[1] = gem_context_create(fd);
> > +     get_freq(fd, ctx[1], &cur_min, &cur_max);
> > +     igt_assert_eq(cur_min, min);
> > +     igt_assert_eq(cur_max, max);
> > +
> > +     set_freq(fd, ctx[1], max, max);
> > +     get_freq(fd, ctx[0], &cur_min, &cur_max);
> > +     igt_assert_eq(cur_min, min);
> > +     igt_assert_eq(cur_max, max);
> > +
> > +     get_freq(fd, 0, &cur_min, &cur_max);
> > +     igt_assert_eq(cur_min, min);
> > +     igt_assert_eq(cur_max, min);
> > +
> > +     get_freq(fd, ctx[1], &cur_min, &cur_max);
> > +     igt_assert_eq(cur_min, max);
> > +     igt_assert_eq(cur_max, max);
> > +     gem_context_destroy(fd, ctx[1]);
> > +
> > +     get_freq(fd, ctx[0], &cur_min, &cur_max);
> There is no set_freq between earlier get_freq and this one for ctx[0] so 
> we can skip one.

The point of this test is to say for sure that actions on one context do
not affect another.

> > +     igt_assert_eq(cur_min, min);
> > +     igt_assert_eq(cur_max, max);
> > +     gem_context_destroy(fd, ctx[0]);
> Need to restore min/max for default context?

No, we assert it isn't changed.

So you think we should assert again to be sure.
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* Re: [PATCH igt] igt: Add gem_ctx_freq to exercise requesting freq on a ctx
  2018-03-09 21:35 ` Chris Wilson
  2018-03-12 21:13   ` Antonio Argenziano
@ 2018-03-13 12:38   ` Sagar Arun Kamble
  2018-03-13 12:50     ` Chris Wilson
  1 sibling, 1 reply; 36+ messages in thread
From: Sagar Arun Kamble @ 2018-03-13 12:38 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx; +Cc: Praveen Paneri



On 3/10/2018 3:05 AM, Chris Wilson wrote:
> Exercise some new API that allows applications to request that
> individual contexts are executed within a desired frequency range.
>
> v2: Split single/continuous set_freq subtests
> v3: Do an up/down ramp for individual freq request, check nothing
> changes after each invalid request
> v4: Check the frequencies reported by the kernel across the entire
> range.
> v5: Rewrite sandwich to create a sandwich between multiple concurrent
> engines.
>
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> Cc: Praveen Paneri <praveen.paneri@intel.com>
> Cc: Sagar A Kamble <sagar.a.kamble@intel.com>
> Cc: Antonio Argenziano <antonio.argenziano@intel.com>
<snip>
> +static void single(int fd, const struct intel_execution_engine *e)
> +{
> +#define N_STEPS 10
> +	const unsigned int engine = e->exec_id | e->flags;
> +	uint32_t ctx = gem_context_create(fd);
> +	uint32_t min, max;
> +	double measured;
> +	igt_spin_t *spin;
> +	int pmu;
> +
> +	get_freq(fd, ctx, &min, &max);
> +	igt_info("Min freq: %dMHz; Max freq: %dMHz\n", min, max);
> +
> +	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
> +	igt_require(pmu >= 0);
This igt_require can go to igt_fixture below.
> +
> +	for (int step = 0; step <= 2*N_STEPS; step++) {
> +		int frac = step > N_STEPS ? 2*N_STEPS - step : step;
> +		uint32_t freq = min + (max - min) * frac / N_STEPS;
> +		uint32_t cur, discard;
> +
> +		set_freq(fd, ctx, freq, freq);
> +		get_freq(fd, ctx, &cur, &discard);
> +
> +		gem_quiescent_gpu(fd);
> +		spin = __igt_spin_batch_new(fd, ctx, engine, 0);
> +		usleep(10000);
> +
> +		measured = measure_frequency(pmu, SAMPLE_PERIOD);
> +		igt_debugfs_dump(fd, "i915_rps_boost_info");
> +
> +		igt_spin_batch_free(fd, spin);
> +		igt_info("%s(single): Measured %.1fMHz, expected %dMhz\n",
> +			 e->name, measured, cur);
> +		igt_assert(measured > cur - 100 && measured < cur + 100);
Is this margin of 100Mhz for PMU accuracy?
> +	}
> +	gem_quiescent_gpu(fd);
> +
> +	close(pmu);
> +	gem_context_destroy(fd, ctx);
> +
> +#undef N_STEPS
> +}
> +
> +static void continuous(int fd, const struct intel_execution_engine *e)
> +{
> +#define N_STEPS 10
> +	const unsigned int engine = e->exec_id | e->flags;
> +	uint32_t ctx = gem_context_create(fd);
> +	uint32_t min, max;
> +	double measured;
> +	igt_spin_t *spin;
> +	int pmu;
> +
> +	get_freq(fd, ctx, &min, &max);
> +	igt_info("Min freq: %dMHz; Max freq: %dMHz\n", min, max);
> +
> +	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
> +	igt_require(pmu >= 0);
> +
> +	gem_quiescent_gpu(fd);
> +	spin = __igt_spin_batch_new(fd, ctx, engine, 0);
> +	for (int step = 0; step <= 2*N_STEPS; step++) {
> +		int frac = step > N_STEPS ? 2*N_STEPS - step : step;
> +		uint32_t freq = min + (max - min) * frac / N_STEPS;
> +		uint32_t cur, discard;
> +		igt_spin_t *kick;
> +
> +		set_freq(fd, ctx, freq, freq);
> +		get_freq(fd, ctx, &cur, &discard);
> +
> +		/*
> +		 * When requesting a new frequency on the currently
> +		 * executing context, it does not take effect until the
> +		 * next context switch. In this case, we trigger a lite
> +		 * restore.
> +		 */
> +		kick = __igt_spin_batch_new(fd, ctx, engine, 0);
> +		igt_spin_batch_free(fd, spin);
> +		spin = kick;
> +
> +		usleep(10000);
> +
> +		measured = measure_frequency(pmu, SAMPLE_PERIOD);
> +		igt_debugfs_dump(fd, "i915_rps_boost_info");
> +
> +		igt_info("%s(continuous): Measured %.1fMHz, expected %dMhz\n",
> +			 e->name, measured, cur);
> +		igt_assert(measured > cur - 100 && measured < cur + 100);
> +	}
> +	igt_spin_batch_free(fd, spin);
> +	gem_quiescent_gpu(fd);
> +
> +	close(pmu);
> +	gem_context_destroy(fd, ctx);
> +#undef N_STEPS
> +}
> +
> +static void inflight(int fd, const struct intel_execution_engine *e)
> +{
> +	const unsigned int engine = e->exec_id | e->flags;
> +	uint32_t ctx, min, max, freq, discard;
> +	double measured;
> +	igt_spin_t *plug, *work[2];
> +	int pmu;
> +
> +	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
> +	igt_require(pmu >= 0);
> +
> +	ctx = gem_context_create(fd);
> +	get_freq(fd, ctx, &min, &max);
> +	set_freq(fd, ctx, min, min);
> +
> +	igt_info("Min freq: %dMHz; Max freq: %dMHz\n", min, max);
> +
> +	gem_quiescent_gpu(fd);
> +	plug = igt_spin_batch_new(fd, ctx, engine, 0);
> +	gem_context_destroy(fd, ctx);
> +	for (int n = 0; n < 16; n++) {
> +		struct drm_i915_gem_exec_object2 obj = {
> +			.handle = plug->handle,
> +		};
> +		struct drm_i915_gem_execbuffer2 eb = {
> +			.buffer_count = 1,
> +			.buffers_ptr = to_user_pointer(&obj),
> +			.flags = engine,
> +			.rsvd1 = gem_context_create(fd),
> +		};
> +		set_freq(fd, eb.rsvd1, min, min);
> +		gem_execbuf(fd, &eb);
> +		gem_context_destroy(fd, eb.rsvd1);
> +	}
> +	measured = measure_frequency(pmu, SAMPLE_PERIOD);
> +	igt_debugfs_dump(fd, "i915_rps_boost_info");
> +	igt_info("%s(plug): Measured %.1fMHz, expected %dMhz\n",
> +		 e->name, measured, min);
> +	igt_assert(measured > min - 100 && measured < min + 100);
> +
> +	ctx = gem_context_create(fd);
> +	set_freq(fd, ctx, max, max);
this set_freq can be removed.
> +	work[0] = __igt_spin_batch_new(fd, ctx, engine, 0);
> +
> +	/* work is now queued but not executing */
> +	freq = (max + min) / 2;
> +	set_freq(fd, ctx, freq, freq);
> +	get_freq(fd, ctx, &freq, &discard);
> +	gem_context_destroy(fd, ctx);
> +
> +	ctx = gem_context_create(fd);
> +	set_freq(fd, ctx, max, max);
> +	work[1] = __igt_spin_batch_new(fd, ctx, engine, 0);
> +	gem_context_destroy(fd, ctx);
> +
> +	igt_spin_batch_end(plug);
> +	do
> +		usleep(10000);
> +	while (gem_bo_busy(fd, plug->handle));
> +	igt_spin_batch_free(fd, plug);
> +
> +	/* Now work will execute */
> +	measured = measure_frequency(pmu, SAMPLE_PERIOD);
> +	igt_debugfs_dump(fd, "i915_engine_info");
> +	igt_debugfs_dump(fd, "i915_rps_boost_info");
> +	igt_info("%s(work0): Measured %.1fMHz, expected %dMhz\n",
> +		 e->name, measured, freq);
> +	igt_assert(measured > freq - 100 && measured < freq + 100);
> +
> +	igt_spin_batch_end(work[0]);
> +	do
> +		usleep(10000);
> +	while (gem_bo_busy(fd, work[0]->handle));
> +	igt_spin_batch_free(fd, work[0]);
> +
> +	measured = measure_frequency(pmu, SAMPLE_PERIOD);
> +	igt_debugfs_dump(fd, "i915_engine_info");
> +	igt_debugfs_dump(fd, "i915_rps_boost_info");
> +	igt_info("%s(work1): Measured %.1fMHz, expected %dMhz\n",
> +		 e->name, measured, max);
> +	igt_assert(measured > max - 100 && measured < max + 100);
> +
> +	igt_spin_batch_free(fd, work[1]);
> +	close(pmu);
> +	gem_quiescent_gpu(fd);
> +}
> +
> +static void sandwich_engine(int fd, unsigned int engine, int timeout)
> +{
> +	uint32_t ctx = gem_context_create(fd);
> +	uint32_t min, max;
> +	int pmu;
> +
> +	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
> +	igt_require(pmu >= 0);
> +
> +	get_freq(fd, ctx, &min, &max);
> +
> +	igt_until_timeout(timeout) {
> +		uint32_t range[2];
> +		igt_spin_t *spin;
> +		double measured;
> +
> +		/* make sure we keep an overlap between all engines */
> +		range[0] = min + (rand() % (max - min) / 2);
> +		range[1] = max - (rand() % (max - min) / 2);
> +
> +		set_freq(fd, ctx, range[0], range[1]);
> +		get_freq(fd, ctx, &range[0], &range[1]);
> +
> +		spin = __igt_spin_batch_new(fd, ctx, engine, 0);
> +
> +		usleep(10000);
> +		measured = measure_frequency(pmu, SAMPLE_PERIOD);
> +		igt_spin_batch_free(fd, spin);
> +
> +		igt_assert(measured >= range[0] - 100 &&
> +			   measured <= range[1] + 100);
> +	}
> +
> +	gem_context_destroy(fd, ctx);
> +	close(pmu);
> +}
> +
> +static void sandwich(int fd, int timeout)
> +{
> +	unsigned int engine;
> +
> +	for_each_physical_engine(fd, engine) {
> +		igt_fork(child, 1)
> +			sandwich_engine(fd, engine, timeout);
> +	}
> +
> +	igt_waitchildren();
> +	gem_quiescent_gpu(fd);
> +}
> +
> +static void pwm(int fd, unsigned int *engines, unsigned int nengine, int link)
> +{
> +	uint32_t ctx[nengine];
> +
> +	fcntl(link, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
> +
> +	for (unsigned int n = 0; n < nengine; n++)
> +		ctx[n] = gem_context_create(fd);
> +
> +	do {
> +		igt_spin_t *spin;
> +		struct {
> +			uint32_t engine;
> +			uint32_t min;
> +			uint32_t max;
> +		} req;
> +
> +		while (read(link, &req, sizeof(req)) > 0) {
> +			if ((req.engine | req.min | req.max) == 0)
> +				goto out;
> +
> +			igt_assert(req.engine < nengine);
> +			set_freq(fd, ctx[req.engine], req.min, req.max);
> +		}
> +
> +		/* Create a 20% load using busy spinners */
> +		spin = __igt_spin_batch_new(fd, ctx[0], engines[0], 0);
> +		for (unsigned int n = 1; n < nengine; n++) {
> +			struct drm_i915_gem_exec_object2 obj = {
> +				.handle = spin->handle,
> +			};
> +			struct drm_i915_gem_execbuffer2 eb = {
> +				.buffer_count = 1,
> +				.buffers_ptr = to_user_pointer(&obj),
> +				.flags = engines[n],
> +				.rsvd1 = ctx[n],
> +			};
> +			gem_execbuf(fd, &eb);
> +		}
> +		usleep(100);
> +		igt_spin_batch_end(spin);
> +
> +		do
> +			usleep(10);
> +		while (gem_bo_busy(fd, spin->handle));
> +		igt_spin_batch_free(fd, spin);
> +		usleep(400);
> +	} while (1);
> +
> +out:
> +	for (unsigned int n = 0; n < nengine; n++)
> +		gem_context_destroy(fd, ctx[n]);
> +}
> +
> +static void smoketest(int fd, int timeout)
> +{
> +	unsigned int engines[16];
> +	unsigned int nengine;
> +	unsigned int engine;
> +	uint32_t min[16], max[16];
> +	int pmu, link[2];
> +
> +	get_freq(fd, 0, &min[0], &max[0]);
> +
> +	nengine = 0;
> +	for_each_physical_engine(fd, engine) {
> +		if (nengine == ARRAY_SIZE(engines) - 1)
> +			break;
> +
> +		min[nengine] = min[0];
> +		max[nengine] = max[0];
> +		engines[nengine] = engine;
> +		nengine++;
> +	}
> +	igt_require(nengine);
> +
> +	igt_assert(pipe(link) == 0);
> +	igt_fork(child, 1)
> +		pwm(fd, engines, nengine, link[0]);
> +	close(link[0]);
> +
> +	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
> +	igt_require(pmu >= 0);
> +
> +	igt_until_timeout(timeout) {
> +		struct {
> +			uint32_t engine;
> +			uint32_t min;
> +			uint32_t max;
> +		} req;
> +		double measured;
> +		uint32_t ctx;
> +
> +		req.engine = rand() % nengine;
> +
> +		ctx = gem_context_create(fd);
> +		get_freq(fd, ctx, &req.min, &req.max);
> +		req.min = rand() % (req.max - req.min) + req.min;
> +		req.max = rand() % (req.max - req.min) + req.min;
> +		set_freq(fd, ctx, req.min, req.max);
> +		get_freq(fd, ctx, &req.min, &req.max);
> +
> +		igt_debug("Replacing (%d, %d) on engine %x with (%d, %d)\n",
> +			  min[req.engine], max[req.engine], req.engine,
> +			  req.min, req.max);
> +		igt_assert(write(link[1], &req, sizeof(req)) == sizeof(req));
> +		gem_context_destroy(fd, ctx);
> +
> +		min[req.engine] = req.min;
> +		max[req.engine] = req.max;
> +
> +		for (unsigned int n = 0; n < nengine; n++) {
> +			igt_debug("[%d]: [%d, %d]\n", n, min[n], max[n]);
> +			if (min[n] < req.min)
> +				req.min = min[n];
> +			if (max[n] > req.max)
> +				req.max = max[n];
> +		}
I thought policy i915 will be implementing is max of mins and min of maxes.
> +		igt_assert(req.max >= req.min);
> +
> +		usleep(50000);
> +		measured = measure_frequency(pmu, SAMPLE_PERIOD);
> +
> +		if (measured <= req.min - 100 || measured >= req.max + 100)
> +			igt_debugfs_dump(fd, "i915_rps_boost_info");
> +		igt_info("Measured %.1fMHz, expected [%d, %d]Mhz\n",
> +			 measured, req.min, req.max);
> +		igt_assert(measured > req.min - 100 &&
> +			   measured < req.max + 100);
> +	}
> +
> +	do {
> +		struct {
> +			uint32_t engine;
> +			uint32_t min;
> +			uint32_t max;
> +		} req = {};
> +
> +		write(link[1], &req, sizeof(req));
> +		close(link[1]);
> +	} while (0);
> +	igt_waitchildren();
> +	gem_quiescent_gpu(fd);
> +
> +	close(pmu);
> +}
> +
> +static void invalid_context(int fd, uint32_t ctx, uint32_t min, uint32_t max)
> +{
> +	const struct test {
> +		uint32_t min, max;
> +	} tests[] = {
> +		{ min - 50, max - 50 },
> +		{ min - 50, max },
> +		{ min - 50, max + 50 },
> +		{ min, max + 50 },
> +		{ min + 50, max + 50 },
> +
> +		{ min - 50, min - 50 },
> +
> +		{ min - 50, min },
This one is similar to { min -  50, max } where max is in range but min 
is outside.
Similarly on max side. what is the reasoning for these cases?
> +		{ min + 50, min },
> +		{ min, min - 50 },
> +
> +		{ max + 50, max },
> +		{ max, max + 50 },
> +		{ max, max - 50 },
> +
> +		{ max + 50, max + 50 },
> +
> +		{}
> +	};
> +
> +	for (const struct test *t = tests; t->min | t->max; t++) {
> +		uint32_t cur_min, cur_max;
> +
> +		igt_assert_f(__set_freq(fd, ctx, t->min, t->max) == -EINVAL,
> +			     "Failed to reject invalid [%d, %d] (valid range [%d, %d]) on context %d\n",
> +			     t->min, t->max, min, max, ctx);
> +
> +		get_freq(fd, 0, &cur_min, &cur_max);
> +		igt_assert_eq(cur_min, min);
> +		igt_assert_eq(cur_max, max);
> +	}
> +}
> +
> +static void invalid(int fd)
> +{
> +	uint32_t min, max, ctx;
> +
> +	get_freq(fd, 0, &min, &max);
> +
> +	invalid_context(fd, 0, min, max);
> +
> +	ctx = gem_context_create(fd);
> +	invalid_context(fd, ctx, min, max);
> +	gem_context_destroy(fd, ctx);
> +}
> +
> +static void idempotent_context(int fd, uint32_t ctx)
> +{
> +	uint32_t min, max;
> +	uint32_t cur_min, cur_max;
> +
> +	get_freq(fd, ctx, &min, &max);
> +
> +	set_freq(fd, ctx, max, max);
> +	get_freq(fd, ctx, &cur_min, &cur_max);
> +	igt_assert_eq(cur_min, max);
> +	igt_assert_eq(cur_max, max);
> +
> +	set_freq(fd, ctx, min, min);
> +	get_freq(fd, ctx, &cur_min, &cur_max);
> +	igt_assert_eq(cur_min, min);
> +	igt_assert_eq(cur_max, min);
> +
> +	set_freq(fd, ctx, min, max);
> +	get_freq(fd, ctx, &cur_min, &cur_max);
> +	igt_assert_eq(cur_min, min);
> +	igt_assert_eq(cur_max, max);
> +}
> +
> +static void idempotent(int fd)
> +{
> +	uint32_t ctx;
> +
> +	idempotent_context(fd, 0);
> +
> +	ctx = gem_context_create(fd);
> +	idempotent_context(fd, ctx);
> +	gem_context_destroy(fd, ctx);
> +}
> +
> +static void range_context(int fd, uint32_t ctx)
> +{
> +	uint32_t min, max;
> +	uint32_t cur_min, cur_max;
> +
> +	get_freq(fd, ctx, &min, &max);
> +
> +	for (uint32_t freq = min; freq <= max; freq++) {
> +		set_freq(fd, ctx, freq, freq);
> +		get_freq(fd, ctx, &cur_min, &cur_max);
> +
> +		igt_assert(cur_min >= min);
> +		igt_assert(cur_max <= max);
> +	}
> +
> +	set_freq(fd, ctx, min, max);
> +	get_freq(fd, ctx, &cur_min, &cur_max);
> +	igt_assert_eq(cur_min, min);
> +	igt_assert_eq(cur_max, max);
> +}
> +
> +static void range(int fd)
> +{
> +	uint32_t ctx;
> +
> +	range_context(fd, 0);
> +
> +	ctx = gem_context_create(fd);
> +	range_context(fd, ctx);
> +	gem_context_destroy(fd, ctx);
> +}
> +
> +static void independent(int fd)
> +{
> +	uint32_t min, max;
> +	uint32_t cur_min, cur_max;
> +	uint32_t ctx[2];
> +
> +	get_freq(fd, 0, &min, &max);
> +
> +	set_freq(fd, 0, max, max);
> +	ctx[0] = gem_context_create(fd);
> +	get_freq(fd, ctx[0], &cur_min, &cur_max);
> +	igt_assert_eq(cur_min, min);
> +	igt_assert_eq(cur_max, max);
> +
> +	set_freq(fd, 0, min, min);
> +	get_freq(fd, ctx[0], &cur_min, &cur_max);
> +	igt_assert_eq(cur_min, min);
> +	igt_assert_eq(cur_max, max);
> +
> +	ctx[1] = gem_context_create(fd);
> +	get_freq(fd, ctx[1], &cur_min, &cur_max);
> +	igt_assert_eq(cur_min, min);
> +	igt_assert_eq(cur_max, max);
> +
> +	set_freq(fd, ctx[1], max, max);
> +	get_freq(fd, ctx[0], &cur_min, &cur_max);
> +	igt_assert_eq(cur_min, min);
> +	igt_assert_eq(cur_max, max);
> +
> +	get_freq(fd, 0, &cur_min, &cur_max);
> +	igt_assert_eq(cur_min, min);
> +	igt_assert_eq(cur_max, min);
> +
> +	get_freq(fd, ctx[1], &cur_min, &cur_max);
> +	igt_assert_eq(cur_min, max);
> +	igt_assert_eq(cur_max, max);
> +	gem_context_destroy(fd, ctx[1]);
> +
> +	get_freq(fd, ctx[0], &cur_min, &cur_max);
There is no set_freq between earlier get_freq and this one for ctx[0] so 
we can skip one.
> +	igt_assert_eq(cur_min, min);
> +	igt_assert_eq(cur_max, max);
> +	gem_context_destroy(fd, ctx[0]);
Need to restore min/max for default context?
> +}
> +
> +static bool has_ctx_freq(int fd)
> +{
> +	struct drm_i915_gem_context_param param = {
> +		.param = LOCAL_CONTEXT_PARAM_FREQUENCY,
> +	};
> +
> +	return __gem_context_get_param(fd, &param) == 0;
> +}
> +
> +igt_main
> +{
> +	const struct intel_execution_engine *e;
> +	int fd = -1;
> +
> +	igt_fixture {
> +		fd = drm_open_driver(DRIVER_INTEL);
> +		igt_require_gem(fd);
> +
> +		igt_require(has_ctx_freq(fd));
> +	}
> +
> +	igt_subtest("invalid")
> +		invalid(fd);
> +
> +	igt_subtest("idempotent")
> +		idempotent(fd);
> +
> +	igt_subtest("range")
> +		range(fd);
> +
> +	igt_subtest("independent")
> +		independent(fd);
> +
> +	igt_skip_on_simulation();
> +
> +	for (e = intel_execution_engines; e->name; e++) {
> +		igt_subtest_group {
> +			igt_fixture {
> +				gem_require_ring(fd, e->exec_id | e->flags);
> +			}
> +
> +			igt_subtest_f("%s-single", e->name)
> +				single(fd, e);
> +			igt_subtest_f("%s-continuous", e->name)
> +				continuous(fd, e);
> +			igt_subtest_f("%s-inflight", e->name)
> +				inflight(fd, e);
> +		}
> +	}
> +
> +	igt_subtest("sandwich")
> +		sandwich(fd, 20);
> +
> +	igt_subtest("smoketest")
> +		smoketest(fd, 20);
> +}
> diff --git a/tests/meson.build b/tests/meson.build
> index 6e776bb7..fa5e3169 100644
> --- a/tests/meson.build
> +++ b/tests/meson.build
> @@ -36,6 +36,7 @@ test_progs = [
>   	'gem_ctx_create',
>   	'gem_ctx_exec',
>   	'gem_ctx_isolation',
> +	'gem_ctx_freq',
this should be one entry above
>   	'gem_ctx_param',
>   	'gem_ctx_switch',
>   	'gem_ctx_thrash',

-- 
Thanks,
Sagar

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

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

* Re: [PATCH igt] igt: Add gem_ctx_freq to exercise requesting freq on a ctx
  2018-03-09 21:35 ` Chris Wilson
@ 2018-03-12 21:13   ` Antonio Argenziano
  2018-03-13 12:38   ` Sagar Arun Kamble
  1 sibling, 0 replies; 36+ messages in thread
From: Antonio Argenziano @ 2018-03-12 21:13 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx; +Cc: Praveen Paneri



On 09/03/18 13:35, Chris Wilson wrote:
> Exercise some new API that allows applications to request that
> individual contexts are executed within a desired frequency range.
> 
> v2: Split single/continuous set_freq subtests
> v3: Do an up/down ramp for individual freq request, check nothing
> changes after each invalid request
> v4: Check the frequencies reported by the kernel across the entire
> range.
> v5: Rewrite sandwich to create a sandwich between multiple concurrent
> engines.
> 
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> Cc: Praveen Paneri <praveen.paneri@intel.com>
> Cc: Sagar A Kamble <sagar.a.kamble@intel.com>
> Cc: Antonio Argenziano <antonio.argenziano@intel.com>

LGTM.

Reviewed-by: Antonio Argenziano <antonio.argenziano@intel.com>
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* [PATCH igt] igt: Add gem_ctx_freq to exercise requesting freq on a ctx
  2018-03-08 17:13 [PATCH igt] " Chris Wilson
  2018-03-09  0:45 ` Antonio Argenziano
  2018-03-09 13:46 ` Chris Wilson
@ 2018-03-09 21:35 ` Chris Wilson
  2018-03-12 21:13   ` Antonio Argenziano
  2018-03-13 12:38   ` Sagar Arun Kamble
  2018-03-13 13:26 ` Chris Wilson
  2018-03-13 13:58 ` Chris Wilson
  4 siblings, 2 replies; 36+ messages in thread
From: Chris Wilson @ 2018-03-09 21:35 UTC (permalink / raw)
  To: intel-gfx; +Cc: Praveen Paneri

Exercise some new API that allows applications to request that
individual contexts are executed within a desired frequency range.

v2: Split single/continuous set_freq subtests
v3: Do an up/down ramp for individual freq request, check nothing
changes after each invalid request
v4: Check the frequencies reported by the kernel across the entire
range.
v5: Rewrite sandwich to create a sandwich between multiple concurrent
engines.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Praveen Paneri <praveen.paneri@intel.com>
Cc: Sagar A Kamble <sagar.a.kamble@intel.com>
Cc: Antonio Argenziano <antonio.argenziano@intel.com>
---
 tests/Makefile.am      |   1 +
 tests/Makefile.sources |   1 +
 tests/gem_ctx_freq.c   | 691 +++++++++++++++++++++++++++++++++++++++++++++++++
 tests/meson.build      |   1 +
 4 files changed, 694 insertions(+)
 create mode 100644 tests/gem_ctx_freq.c

diff --git a/tests/Makefile.am b/tests/Makefile.am
index dbc7be72..389f7fc7 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -104,6 +104,7 @@ drm_import_export_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 drm_import_export_LDADD = $(LDADD) -lpthread
 gem_close_race_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_close_race_LDADD = $(LDADD) -lpthread
+gem_ctx_freq_LDADD = $(LDADD) $(top_builddir)/lib/libigt_perf.la
 gem_ctx_thrash_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_ctx_thrash_LDADD = $(LDADD) -lpthread
 gem_exec_parallel_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index 4a81ac4a..3d079c42 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -58,6 +58,7 @@ TESTS_progs = \
 	gem_ctx_bad_exec \
 	gem_ctx_create \
 	gem_ctx_exec \
+	gem_ctx_freq \
 	gem_ctx_isolation \
 	gem_ctx_param \
 	gem_ctx_switch \
diff --git a/tests/gem_ctx_freq.c b/tests/gem_ctx_freq.c
new file mode 100644
index 00000000..fc5df3d9
--- /dev/null
+++ b/tests/gem_ctx_freq.c
@@ -0,0 +1,691 @@
+/*
+ * 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 <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 <time.h>
+
+#include "igt.h"
+#include "igt_perf.h"
+
+#define LOCAL_CONTEXT_PARAM_FREQUENCY 7
+
+#define SAMPLE_PERIOD (USEC_PER_SEC / 10)
+
+static int __set_freq(int fd, uint32_t ctx, uint32_t min, uint32_t max)
+{
+	struct drm_i915_gem_context_param param = {
+		.ctx_id = ctx,
+		.param = LOCAL_CONTEXT_PARAM_FREQUENCY,
+		.value = (uint64_t)max << 32 | min,
+	};
+
+	return __gem_context_set_param(fd, &param);
+}
+
+static void set_freq(int fd, uint32_t ctx, uint32_t min, uint32_t max)
+{
+	igt_assert_eq(__set_freq(fd, ctx, min, max), 0);
+}
+
+static void get_freq(int fd, uint32_t ctx, uint32_t *min, uint32_t *max)
+{
+	struct drm_i915_gem_context_param param = {
+		.ctx_id = ctx,
+		.param = LOCAL_CONTEXT_PARAM_FREQUENCY,
+	};
+
+	gem_context_get_param(fd, &param);
+
+	*min = param.value & 0xffffffff;
+	*max = param.value >> 32;
+}
+
+static double measure_frequency(int pmu, int period_us)
+{
+	uint64_t data[2];
+	uint64_t d_t, d_v;
+
+	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 * 1e9 / d_t;
+}
+
+static void single(int fd, const struct intel_execution_engine *e)
+{
+#define N_STEPS 10
+	const unsigned int engine = e->exec_id | e->flags;
+	uint32_t ctx = gem_context_create(fd);
+	uint32_t min, max;
+	double measured;
+	igt_spin_t *spin;
+	int pmu;
+
+	get_freq(fd, ctx, &min, &max);
+	igt_info("Min freq: %dMHz; Max freq: %dMHz\n", min, max);
+
+	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
+	igt_require(pmu >= 0);
+
+	for (int step = 0; step <= 2*N_STEPS; step++) {
+		int frac = step > N_STEPS ? 2*N_STEPS - step : step;
+		uint32_t freq = min + (max - min) * frac / N_STEPS;
+		uint32_t cur, discard;
+
+		set_freq(fd, ctx, freq, freq);
+		get_freq(fd, ctx, &cur, &discard);
+
+		gem_quiescent_gpu(fd);
+		spin = __igt_spin_batch_new(fd, ctx, engine, 0);
+		usleep(10000);
+
+		measured = measure_frequency(pmu, SAMPLE_PERIOD);
+		igt_debugfs_dump(fd, "i915_rps_boost_info");
+
+		igt_spin_batch_free(fd, spin);
+		igt_info("%s(single): Measured %.1fMHz, expected %dMhz\n",
+			 e->name, measured, cur);
+		igt_assert(measured > cur - 100 && measured < cur + 100);
+	}
+	gem_quiescent_gpu(fd);
+
+	close(pmu);
+	gem_context_destroy(fd, ctx);
+
+#undef N_STEPS
+}
+
+static void continuous(int fd, const struct intel_execution_engine *e)
+{
+#define N_STEPS 10
+	const unsigned int engine = e->exec_id | e->flags;
+	uint32_t ctx = gem_context_create(fd);
+	uint32_t min, max;
+	double measured;
+	igt_spin_t *spin;
+	int pmu;
+
+	get_freq(fd, ctx, &min, &max);
+	igt_info("Min freq: %dMHz; Max freq: %dMHz\n", min, max);
+
+	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
+	igt_require(pmu >= 0);
+
+	gem_quiescent_gpu(fd);
+	spin = __igt_spin_batch_new(fd, ctx, engine, 0);
+	for (int step = 0; step <= 2*N_STEPS; step++) {
+		int frac = step > N_STEPS ? 2*N_STEPS - step : step;
+		uint32_t freq = min + (max - min) * frac / N_STEPS;
+		uint32_t cur, discard;
+		igt_spin_t *kick;
+
+		set_freq(fd, ctx, freq, freq);
+		get_freq(fd, ctx, &cur, &discard);
+
+		/*
+		 * When requesting a new frequency on the currently
+		 * executing context, it does not take effect until the
+		 * next context switch. In this case, we trigger a lite
+		 * restore.
+		 */
+		kick = __igt_spin_batch_new(fd, ctx, engine, 0);
+		igt_spin_batch_free(fd, spin);
+		spin = kick;
+
+		usleep(10000);
+
+		measured = measure_frequency(pmu, SAMPLE_PERIOD);
+		igt_debugfs_dump(fd, "i915_rps_boost_info");
+
+		igt_info("%s(continuous): Measured %.1fMHz, expected %dMhz\n",
+			 e->name, measured, cur);
+		igt_assert(measured > cur - 100 && measured < cur + 100);
+	}
+	igt_spin_batch_free(fd, spin);
+	gem_quiescent_gpu(fd);
+
+	close(pmu);
+	gem_context_destroy(fd, ctx);
+#undef N_STEPS
+}
+
+static void inflight(int fd, const struct intel_execution_engine *e)
+{
+	const unsigned int engine = e->exec_id | e->flags;
+	uint32_t ctx, min, max, freq, discard;
+	double measured;
+	igt_spin_t *plug, *work[2];
+	int pmu;
+
+	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
+	igt_require(pmu >= 0);
+
+	ctx = gem_context_create(fd);
+	get_freq(fd, ctx, &min, &max);
+	set_freq(fd, ctx, min, min);
+
+	igt_info("Min freq: %dMHz; Max freq: %dMHz\n", min, max);
+
+	gem_quiescent_gpu(fd);
+	plug = igt_spin_batch_new(fd, ctx, engine, 0);
+	gem_context_destroy(fd, ctx);
+	for (int n = 0; n < 16; n++) {
+		struct drm_i915_gem_exec_object2 obj = {
+			.handle = plug->handle,
+		};
+		struct drm_i915_gem_execbuffer2 eb = {
+			.buffer_count = 1,
+			.buffers_ptr = to_user_pointer(&obj),
+			.flags = engine,
+			.rsvd1 = gem_context_create(fd),
+		};
+		set_freq(fd, eb.rsvd1, min, min);
+		gem_execbuf(fd, &eb);
+		gem_context_destroy(fd, eb.rsvd1);
+	}
+	measured = measure_frequency(pmu, SAMPLE_PERIOD);
+	igt_debugfs_dump(fd, "i915_rps_boost_info");
+	igt_info("%s(plug): Measured %.1fMHz, expected %dMhz\n",
+		 e->name, measured, min);
+	igt_assert(measured > min - 100 && measured < min + 100);
+
+	ctx = gem_context_create(fd);
+	set_freq(fd, ctx, max, max);
+	work[0] = __igt_spin_batch_new(fd, ctx, engine, 0);
+
+	/* work is now queued but not executing */
+	freq = (max + min) / 2;
+	set_freq(fd, ctx, freq, freq);
+	get_freq(fd, ctx, &freq, &discard);
+	gem_context_destroy(fd, ctx);
+
+	ctx = gem_context_create(fd);
+	set_freq(fd, ctx, max, max);
+	work[1] = __igt_spin_batch_new(fd, ctx, engine, 0);
+	gem_context_destroy(fd, ctx);
+
+	igt_spin_batch_end(plug);
+	do
+		usleep(10000);
+	while (gem_bo_busy(fd, plug->handle));
+	igt_spin_batch_free(fd, plug);
+
+	/* Now work will execute */
+	measured = measure_frequency(pmu, SAMPLE_PERIOD);
+	igt_debugfs_dump(fd, "i915_engine_info");
+	igt_debugfs_dump(fd, "i915_rps_boost_info");
+	igt_info("%s(work0): Measured %.1fMHz, expected %dMhz\n",
+		 e->name, measured, freq);
+	igt_assert(measured > freq - 100 && measured < freq + 100);
+
+	igt_spin_batch_end(work[0]);
+	do
+		usleep(10000);
+	while (gem_bo_busy(fd, work[0]->handle));
+	igt_spin_batch_free(fd, work[0]);
+
+	measured = measure_frequency(pmu, SAMPLE_PERIOD);
+	igt_debugfs_dump(fd, "i915_engine_info");
+	igt_debugfs_dump(fd, "i915_rps_boost_info");
+	igt_info("%s(work1): Measured %.1fMHz, expected %dMhz\n",
+		 e->name, measured, max);
+	igt_assert(measured > max - 100 && measured < max + 100);
+
+	igt_spin_batch_free(fd, work[1]);
+	close(pmu);
+	gem_quiescent_gpu(fd);
+}
+
+static void sandwich_engine(int fd, unsigned int engine, int timeout)
+{
+	uint32_t ctx = gem_context_create(fd);
+	uint32_t min, max;
+	int pmu;
+
+	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
+	igt_require(pmu >= 0);
+
+	get_freq(fd, ctx, &min, &max);
+
+	igt_until_timeout(timeout) {
+		uint32_t range[2];
+		igt_spin_t *spin;
+		double measured;
+
+		/* make sure we keep an overlap between all engines */
+		range[0] = min + (rand() % (max - min) / 2);
+		range[1] = max - (rand() % (max - min) / 2);
+
+		set_freq(fd, ctx, range[0], range[1]);
+		get_freq(fd, ctx, &range[0], &range[1]);
+
+		spin = __igt_spin_batch_new(fd, ctx, engine, 0);
+
+		usleep(10000);
+		measured = measure_frequency(pmu, SAMPLE_PERIOD);
+		igt_spin_batch_free(fd, spin);
+
+		igt_assert(measured >= range[0] - 100 &&
+			   measured <= range[1] + 100);
+	}
+
+	gem_context_destroy(fd, ctx);
+	close(pmu);
+}
+
+static void sandwich(int fd, int timeout)
+{
+	unsigned int engine;
+
+	for_each_physical_engine(fd, engine) {
+		igt_fork(child, 1)
+			sandwich_engine(fd, engine, timeout);
+	}
+
+	igt_waitchildren();
+	gem_quiescent_gpu(fd);
+}
+
+static void pwm(int fd, unsigned int *engines, unsigned int nengine, int link)
+{
+	uint32_t ctx[nengine];
+
+	fcntl(link, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
+
+	for (unsigned int n = 0; n < nengine; n++)
+		ctx[n] = gem_context_create(fd);
+
+	do {
+		igt_spin_t *spin;
+		struct {
+			uint32_t engine;
+			uint32_t min;
+			uint32_t max;
+		} req;
+
+		while (read(link, &req, sizeof(req)) > 0) {
+			if ((req.engine | req.min | req.max) == 0)
+				goto out;
+
+			igt_assert(req.engine < nengine);
+			set_freq(fd, ctx[req.engine], req.min, req.max);
+		}
+
+		/* Create a 20% load using busy spinners */
+		spin = __igt_spin_batch_new(fd, ctx[0], engines[0], 0);
+		for (unsigned int n = 1; n < nengine; n++) {
+			struct drm_i915_gem_exec_object2 obj = {
+				.handle = spin->handle,
+			};
+			struct drm_i915_gem_execbuffer2 eb = {
+				.buffer_count = 1,
+				.buffers_ptr = to_user_pointer(&obj),
+				.flags = engines[n],
+				.rsvd1 = ctx[n],
+			};
+			gem_execbuf(fd, &eb);
+		}
+		usleep(100);
+		igt_spin_batch_end(spin);
+
+		do
+			usleep(10);
+		while (gem_bo_busy(fd, spin->handle));
+		igt_spin_batch_free(fd, spin);
+		usleep(400);
+	} while (1);
+
+out:
+	for (unsigned int n = 0; n < nengine; n++)
+		gem_context_destroy(fd, ctx[n]);
+}
+
+static void smoketest(int fd, int timeout)
+{
+	unsigned int engines[16];
+	unsigned int nengine;
+	unsigned int engine;
+	uint32_t min[16], max[16];
+	int pmu, link[2];
+
+	get_freq(fd, 0, &min[0], &max[0]);
+
+	nengine = 0;
+	for_each_physical_engine(fd, engine) {
+		if (nengine == ARRAY_SIZE(engines) - 1)
+			break;
+
+		min[nengine] = min[0];
+		max[nengine] = max[0];
+		engines[nengine] = engine;
+		nengine++;
+	}
+	igt_require(nengine);
+
+	igt_assert(pipe(link) == 0);
+	igt_fork(child, 1)
+		pwm(fd, engines, nengine, link[0]);
+	close(link[0]);
+
+	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
+	igt_require(pmu >= 0);
+
+	igt_until_timeout(timeout) {
+		struct {
+			uint32_t engine;
+			uint32_t min;
+			uint32_t max;
+		} req;
+		double measured;
+		uint32_t ctx;
+
+		req.engine = rand() % nengine;
+
+		ctx = gem_context_create(fd);
+		get_freq(fd, ctx, &req.min, &req.max);
+		req.min = rand() % (req.max - req.min) + req.min;
+		req.max = rand() % (req.max - req.min) + req.min;
+		set_freq(fd, ctx, req.min, req.max);
+		get_freq(fd, ctx, &req.min, &req.max);
+
+		igt_debug("Replacing (%d, %d) on engine %x with (%d, %d)\n",
+			  min[req.engine], max[req.engine], req.engine,
+			  req.min, req.max);
+		igt_assert(write(link[1], &req, sizeof(req)) == sizeof(req));
+		gem_context_destroy(fd, ctx);
+
+		min[req.engine] = req.min;
+		max[req.engine] = req.max;
+
+		for (unsigned int n = 0; n < nengine; n++) {
+			igt_debug("[%d]: [%d, %d]\n", n, min[n], max[n]);
+			if (min[n] < req.min)
+				req.min = min[n];
+			if (max[n] > req.max)
+				req.max = max[n];
+		}
+		igt_assert(req.max >= req.min);
+
+		usleep(50000);
+		measured = measure_frequency(pmu, SAMPLE_PERIOD);
+
+		if (measured <= req.min - 100 || measured >= req.max + 100)
+			igt_debugfs_dump(fd, "i915_rps_boost_info");
+		igt_info("Measured %.1fMHz, expected [%d, %d]Mhz\n",
+			 measured, req.min, req.max);
+		igt_assert(measured > req.min - 100 &&
+			   measured < req.max + 100);
+	}
+
+	do {
+		struct {
+			uint32_t engine;
+			uint32_t min;
+			uint32_t max;
+		} req = {};
+
+		write(link[1], &req, sizeof(req));
+		close(link[1]);
+	} while (0);
+	igt_waitchildren();
+	gem_quiescent_gpu(fd);
+
+	close(pmu);
+}
+
+static void invalid_context(int fd, uint32_t ctx, uint32_t min, uint32_t max)
+{
+	const struct test {
+		uint32_t min, max;
+	} tests[] = {
+		{ min - 50, max - 50 },
+		{ min - 50, max },
+		{ min - 50, max + 50 },
+		{ min, max + 50 },
+		{ min + 50, max + 50 },
+
+		{ min - 50, min - 50 },
+
+		{ min - 50, min },
+		{ min + 50, min },
+		{ min, min - 50 },
+
+		{ max + 50, max },
+		{ max, max + 50 },
+		{ max, max - 50 },
+
+		{ max + 50, max + 50 },
+
+		{}
+	};
+
+	for (const struct test *t = tests; t->min | t->max; t++) {
+		uint32_t cur_min, cur_max;
+
+		igt_assert_f(__set_freq(fd, ctx, t->min, t->max) == -EINVAL,
+			     "Failed to reject invalid [%d, %d] (valid range [%d, %d]) on context %d\n",
+			     t->min, t->max, min, max, ctx);
+
+		get_freq(fd, 0, &cur_min, &cur_max);
+		igt_assert_eq(cur_min, min);
+		igt_assert_eq(cur_max, max);
+	}
+}
+
+static void invalid(int fd)
+{
+	uint32_t min, max, ctx;
+
+	get_freq(fd, 0, &min, &max);
+
+	invalid_context(fd, 0, min, max);
+
+	ctx = gem_context_create(fd);
+	invalid_context(fd, ctx, min, max);
+	gem_context_destroy(fd, ctx);
+}
+
+static void idempotent_context(int fd, uint32_t ctx)
+{
+	uint32_t min, max;
+	uint32_t cur_min, cur_max;
+
+	get_freq(fd, ctx, &min, &max);
+
+	set_freq(fd, ctx, max, max);
+	get_freq(fd, ctx, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, max);
+	igt_assert_eq(cur_max, max);
+
+	set_freq(fd, ctx, min, min);
+	get_freq(fd, ctx, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, min);
+
+	set_freq(fd, ctx, min, max);
+	get_freq(fd, ctx, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+}
+
+static void idempotent(int fd)
+{
+	uint32_t ctx;
+
+	idempotent_context(fd, 0);
+
+	ctx = gem_context_create(fd);
+	idempotent_context(fd, ctx);
+	gem_context_destroy(fd, ctx);
+}
+
+static void range_context(int fd, uint32_t ctx)
+{
+	uint32_t min, max;
+	uint32_t cur_min, cur_max;
+
+	get_freq(fd, ctx, &min, &max);
+
+	for (uint32_t freq = min; freq <= max; freq++) {
+		set_freq(fd, ctx, freq, freq);
+		get_freq(fd, ctx, &cur_min, &cur_max);
+
+		igt_assert(cur_min >= min);
+		igt_assert(cur_max <= max);
+	}
+
+	set_freq(fd, ctx, min, max);
+	get_freq(fd, ctx, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+}
+
+static void range(int fd)
+{
+	uint32_t ctx;
+
+	range_context(fd, 0);
+
+	ctx = gem_context_create(fd);
+	range_context(fd, ctx);
+	gem_context_destroy(fd, ctx);
+}
+
+static void independent(int fd)
+{
+	uint32_t min, max;
+	uint32_t cur_min, cur_max;
+	uint32_t ctx[2];
+
+	get_freq(fd, 0, &min, &max);
+
+	set_freq(fd, 0, max, max);
+	ctx[0] = gem_context_create(fd);
+	get_freq(fd, ctx[0], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+
+	set_freq(fd, 0, min, min);
+	get_freq(fd, ctx[0], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+
+	ctx[1] = gem_context_create(fd);
+	get_freq(fd, ctx[1], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+
+	set_freq(fd, ctx[1], max, max);
+	get_freq(fd, ctx[0], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+
+	get_freq(fd, 0, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, min);
+
+	get_freq(fd, ctx[1], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, max);
+	igt_assert_eq(cur_max, max);
+	gem_context_destroy(fd, ctx[1]);
+
+	get_freq(fd, ctx[0], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+	gem_context_destroy(fd, ctx[0]);
+}
+
+static bool has_ctx_freq(int fd)
+{
+	struct drm_i915_gem_context_param param = {
+		.param = LOCAL_CONTEXT_PARAM_FREQUENCY,
+	};
+
+	return __gem_context_get_param(fd, &param) == 0;
+}
+
+igt_main
+{
+	const struct intel_execution_engine *e;
+	int fd = -1;
+
+	igt_fixture {
+		fd = drm_open_driver(DRIVER_INTEL);
+		igt_require_gem(fd);
+
+		igt_require(has_ctx_freq(fd));
+	}
+
+	igt_subtest("invalid")
+		invalid(fd);
+
+	igt_subtest("idempotent")
+		idempotent(fd);
+
+	igt_subtest("range")
+		range(fd);
+
+	igt_subtest("independent")
+		independent(fd);
+
+	igt_skip_on_simulation();
+
+	for (e = intel_execution_engines; e->name; e++) {
+		igt_subtest_group {
+			igt_fixture {
+				gem_require_ring(fd, e->exec_id | e->flags);
+			}
+
+			igt_subtest_f("%s-single", e->name)
+				single(fd, e);
+			igt_subtest_f("%s-continuous", e->name)
+				continuous(fd, e);
+			igt_subtest_f("%s-inflight", e->name)
+				inflight(fd, e);
+		}
+	}
+
+	igt_subtest("sandwich")
+		sandwich(fd, 20);
+
+	igt_subtest("smoketest")
+		smoketest(fd, 20);
+}
diff --git a/tests/meson.build b/tests/meson.build
index 6e776bb7..fa5e3169 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -36,6 +36,7 @@ test_progs = [
 	'gem_ctx_create',
 	'gem_ctx_exec',
 	'gem_ctx_isolation',
+	'gem_ctx_freq',
 	'gem_ctx_param',
 	'gem_ctx_switch',
 	'gem_ctx_thrash',
-- 
2.16.2

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

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

* Re: [PATCH igt] igt: Add gem_ctx_freq to exercise requesting freq on a ctx
  2018-03-09 19:15     ` Antonio Argenziano
@ 2018-03-09 20:37       ` Chris Wilson
  0 siblings, 0 replies; 36+ messages in thread
From: Chris Wilson @ 2018-03-09 20:37 UTC (permalink / raw)
  To: Antonio Argenziano, intel-gfx; +Cc: igt-dev, Praveen

Quoting Antonio Argenziano (2018-03-09 19:15:45)
> 
> 
> On 08/03/18 17:03, Chris Wilson wrote:
> > Quoting Antonio Argenziano (2018-03-09 00:45:42)
> >>
> >>
> >> On 08/03/18 09:13, Chris Wilson wrote:
> >>> Exercise some new API that allows applications to request that
> >>> individual contexts are executed within a desired frequency range.
> >>>
> >>> v2: Split single/continuous set_freq subtests
> >>>
> >>> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> >>> Cc: Paneri, Praveen <praveen.paneri@intel.com>
> >>> Cc: Kamble, Sagar A <sagar.a.kamble@intel.com>
> >>> Cc: Antonio Argenziano <antonio.argenziano@intel.com>
> >>> ---
> >>>    tests/Makefile.am      |   1 +
> >>>    tests/Makefile.sources |   1 +
> >>>    tests/gem_ctx_freq.c   | 604 +++++++++++++++++++++++++++++++++++++++++++++++++
> >>>    tests/meson.build      |   1 +
> >>>    4 files changed, 607 insertions(+)
> >>>    create mode 100644 tests/gem_ctx_freq.c
> >>>
> >>
> 
> >>> +             uint32_t cur, discard;
> >>> +
> >>> +             set_freq(fd, ctx, freq, freq);
> >>> +             get_freq(fd, ctx, &cur, &discard);
> >>
> >> igt_assert_eq(freq, cur)?
> > 
> > Not quite. The trick is that the interface is not strictly idempotent,
> > since we pass in MHz, the driver converts that into freq bins and spits
> > it back out to the nearest MHz. So cur is not strictly freq, it just
> > happens that 50MHz is the bin size on gen9.
> > 
> > The idea here is that we grab the adjusted freq from the driver to
> > validate with.
> 
> I see, can we enforce a tolerance? It feels like we are trusting the 
> kernel too much, but if that is the only thing we can do...

for (i = min; i <= max; i++)
	igt_assert(min <= set_and_fetch_freq(i) <= max);

I don't think we want to constrain the ABI any more than that.
But adding that level of check seems ok.

The behaviour is we ask the kernel for a range, the kernel tells us what
range it can provide based on the request. Then we expect that the
kernel upholds that contract. (Except where we make a conflicting
contract with another party, either parallel execution or sysadmin
override.)

Binding ourselves into a tighter contract feels overly prescriptive and
not flexible enough to weasel our way out of bad situations in future.
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* Re: [PATCH igt] igt: Add gem_ctx_freq to exercise requesting freq on a ctx
  2018-03-09  1:03   ` Chris Wilson
@ 2018-03-09 19:15     ` Antonio Argenziano
  2018-03-09 20:37       ` Chris Wilson
  0 siblings, 1 reply; 36+ messages in thread
From: Antonio Argenziano @ 2018-03-09 19:15 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx; +Cc: igt-dev, Praveen



On 08/03/18 17:03, Chris Wilson wrote:
> Quoting Antonio Argenziano (2018-03-09 00:45:42)
>>
>>
>> On 08/03/18 09:13, Chris Wilson wrote:
>>> Exercise some new API that allows applications to request that
>>> individual contexts are executed within a desired frequency range.
>>>
>>> v2: Split single/continuous set_freq subtests
>>>
>>> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
>>> Cc: Paneri, Praveen <praveen.paneri@intel.com>
>>> Cc: Kamble, Sagar A <sagar.a.kamble@intel.com>
>>> Cc: Antonio Argenziano <antonio.argenziano@intel.com>
>>> ---
>>>    tests/Makefile.am      |   1 +
>>>    tests/Makefile.sources |   1 +
>>>    tests/gem_ctx_freq.c   | 604 +++++++++++++++++++++++++++++++++++++++++++++++++
>>>    tests/meson.build      |   1 +
>>>    4 files changed, 607 insertions(+)
>>>    create mode 100644 tests/gem_ctx_freq.c
>>>
>>

>>> +             uint32_t cur, discard;
>>> +
>>> +             set_freq(fd, ctx, freq, freq);
>>> +             get_freq(fd, ctx, &cur, &discard);
>>
>> igt_assert_eq(freq, cur)?
> 
> Not quite. The trick is that the interface is not strictly idempotent,
> since we pass in MHz, the driver converts that into freq bins and spits
> it back out to the nearest MHz. So cur is not strictly freq, it just
> happens that 50MHz is the bin size on gen9.
> 
> The idea here is that we grab the adjusted freq from the driver to
> validate with.

I see, can we enforce a tolerance? It feels like we are trusting the 
kernel too much, but if that is the only thing we can do...

>>> +static void smoketest(int fd, int timeout)
>>> +{
>>> +     unsigned int engines[16];
>>
>> use a macro instead of magic number 16.
> 
> #define THIS_IS_FAR_MORE_MAGIC_THAN_A_MEANINGLESS_BARE_NUMBER_THAT_YOU_SHOULD_NOT_BE_READING_ANYTHING_INTO 16
> /rant

We call that MAX_EGINES in gem_exec_schedule ;).

Thanks,
Antonio

> 
>>> +static void invalid_param(int fd)
>>> +{
>>
>> gem_ctx_param is going to be upset again pretty soon ;).
> 
> Poor thing.
> -Chris
> 
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* Re: [PATCH igt] igt: Add gem_ctx_freq to exercise requesting freq on a ctx
  2018-03-09 17:06   ` Tvrtko Ursulin
@ 2018-03-09 17:24     ` Chris Wilson
  0 siblings, 0 replies; 36+ messages in thread
From: Chris Wilson @ 2018-03-09 17:24 UTC (permalink / raw)
  To: Tvrtko Ursulin, intel-gfx; +Cc: igt-dev, Praveen

Quoting Tvrtko Ursulin (2018-03-09 17:06:45)
> 
> On 09/03/2018 13:46, Chris Wilson wrote:
> > Exercise some new API that allows applications to request that
> > individual contexts are executed within a desired frequency range.
> > 
> > v2: Split single/continuous set_freq subtests
> > v3: Do an up/down ramp for individual freq request, check nothing
> > changes after each invalid request
> > 
> > Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> > Cc: Paneri, Praveen <praveen.paneri@intel.com>
> > Cc: Kamble, Sagar A <sagar.a.kamble@intel.com>
> > Cc: Antonio Argenziano <antonio.argenziano@intel.com>
> > ---
> >   tests/Makefile.am      |   1 +
> >   tests/Makefile.sources |   1 +
> >   tests/gem_ctx_freq.c   | 648 +++++++++++++++++++++++++++++++++++++++++++++++++
> >   tests/meson.build      |   1 +
> >   4 files changed, 651 insertions(+)
> >   create mode 100644 tests/gem_ctx_freq.c
> > 
> 
> [snip]
> 
> > +static void check_invalid(int fd, uint32_t ctx, uint32_t min, uint32_t max)
> > +{
> > +     const struct test {
> > +             uint32_t min, max;
> > +     } tests[] = {
> > +             { min - 50, max - 50 },
> > +             { min - 50, max },
> > +             { min - 50, max + 50 },
> > +             { min, max + 50 },
> > +             { min + 50, max + 50 },
> > +
> > +             { min - 50, min - 50 },
> > +
> > +             { min - 50, min },
> > +             { min + 50, min },
> > +             { min, min - 50 },
> > +
> > +             { max + 50, max },
> > +             { max, max + 50 },
> > +             { max, max - 50 },
> > +
> > +             { max + 50, max + 50 },
> 
> Is unprivileged "{ max, max }" allowed? In other words pin to max 
> frequency by anyone? If so, what happens when all userspace learns about 
> this and wants to use it just so to be lower latency than the other guy?

I've gone with allow, since (a) it's always constrained by the global
user imposed limit and (b) userspace can already keep the gpu at max
frequency by load. At the start I opined that only CAP_SYS_NICE would be
allowed to raise the frequency bounds, but realised that in practice it
is immaterial as they were already running at max frequency anyway.

/*
 * As we constrain the frequency request from the
 * context (application) by the sysadmin imposed limits,
 * it is reasonable to allow the application to
 * specify its preferred range within those limits.
 * That is we do not need to restrict requesting
 * a higher frequency to privileged (CAP_SYS_NICE)
 * processes.
 */

> Or even on our integrated graphics, such userspace would unwisely take 
> away power budget from the CPU. Or could it even harm itself by 
> triggering too much thermal throttling and run slower than if it let the 
> balance be controlled by the system?

It will indeed. Running at max frequency is not a sensible idea for
anything but a few applications (dare we say miners? ;). I thought
compositors might benefit from reduced latency by starting at max,
https://bugs.freedesktop.org/show_bug.cgi?id=102199
but realistically they care more about power consumption and gain most of
the latency reduction from priority sorting and preemption.

On the bright side, we give them a loaded gun with which they can shoot
both feet off. They have to be confident that they do know their
behaviour better than the hw (which for a few will be true). We give
them the means to do so, we do not say it is wise.

> Or will this be tied with the cgroup work to allow only clients allowed 
> by sysadmin to do this?

That's what I think as well. I think we will end up with everything that
can be adjusted via CONTEXT_SETPARAM will be constrained by cgroup.

Once again, we can only look at the integration of schedfreq and CFS as
being the direction the GPUs will also eventually take.
-Chris
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

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

* Re: [PATCH igt] igt: Add gem_ctx_freq to exercise requesting freq on a ctx
  2018-03-09 13:46 ` Chris Wilson
@ 2018-03-09 17:06   ` Tvrtko Ursulin
  2018-03-09 17:24     ` Chris Wilson
  0 siblings, 1 reply; 36+ messages in thread
From: Tvrtko Ursulin @ 2018-03-09 17:06 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx; +Cc: igt-dev, Praveen


On 09/03/2018 13:46, Chris Wilson wrote:
> Exercise some new API that allows applications to request that
> individual contexts are executed within a desired frequency range.
> 
> v2: Split single/continuous set_freq subtests
> v3: Do an up/down ramp for individual freq request, check nothing
> changes after each invalid request
> 
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> Cc: Paneri, Praveen <praveen.paneri@intel.com>
> Cc: Kamble, Sagar A <sagar.a.kamble@intel.com>
> Cc: Antonio Argenziano <antonio.argenziano@intel.com>
> ---
>   tests/Makefile.am      |   1 +
>   tests/Makefile.sources |   1 +
>   tests/gem_ctx_freq.c   | 648 +++++++++++++++++++++++++++++++++++++++++++++++++
>   tests/meson.build      |   1 +
>   4 files changed, 651 insertions(+)
>   create mode 100644 tests/gem_ctx_freq.c
> 

[snip]

> +static void check_invalid(int fd, uint32_t ctx, uint32_t min, uint32_t max)
> +{
> +	const struct test {
> +		uint32_t min, max;
> +	} tests[] = {
> +		{ min - 50, max - 50 },
> +		{ min - 50, max },
> +		{ min - 50, max + 50 },
> +		{ min, max + 50 },
> +		{ min + 50, max + 50 },
> +
> +		{ min - 50, min - 50 },
> +
> +		{ min - 50, min },
> +		{ min + 50, min },
> +		{ min, min - 50 },
> +
> +		{ max + 50, max },
> +		{ max, max + 50 },
> +		{ max, max - 50 },
> +
> +		{ max + 50, max + 50 },

Is unprivileged "{ max, max }" allowed? In other words pin to max 
frequency by anyone? If so, what happens when all userspace learns about 
this and wants to use it just so to be lower latency than the other guy?

Or even on our integrated graphics, such userspace would unwisely take 
away power budget from the CPU. Or could it even harm itself by 
triggering too much thermal throttling and run slower than if it let the 
balance be controlled by the system?

Or will this be tied with the cgroup work to allow only clients allowed 
by sysadmin to do this?

Regards,

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

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

* [PATCH igt] igt: Add gem_ctx_freq to exercise requesting freq on a ctx
  2018-03-08 17:13 [PATCH igt] " Chris Wilson
  2018-03-09  0:45 ` Antonio Argenziano
@ 2018-03-09 13:46 ` Chris Wilson
  2018-03-09 17:06   ` Tvrtko Ursulin
  2018-03-09 21:35 ` Chris Wilson
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 36+ messages in thread
From: Chris Wilson @ 2018-03-09 13:46 UTC (permalink / raw)
  To: intel-gfx; +Cc: Paneri, Kamble, igt-dev, Praveen

Exercise some new API that allows applications to request that
individual contexts are executed within a desired frequency range.

v2: Split single/continuous set_freq subtests
v3: Do an up/down ramp for individual freq request, check nothing
changes after each invalid request

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Paneri, Praveen <praveen.paneri@intel.com>
Cc: Kamble, Sagar A <sagar.a.kamble@intel.com>
Cc: Antonio Argenziano <antonio.argenziano@intel.com>
---
 tests/Makefile.am      |   1 +
 tests/Makefile.sources |   1 +
 tests/gem_ctx_freq.c   | 648 +++++++++++++++++++++++++++++++++++++++++++++++++
 tests/meson.build      |   1 +
 4 files changed, 651 insertions(+)
 create mode 100644 tests/gem_ctx_freq.c

diff --git a/tests/Makefile.am b/tests/Makefile.am
index dbc7be72..389f7fc7 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -104,6 +104,7 @@ drm_import_export_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 drm_import_export_LDADD = $(LDADD) -lpthread
 gem_close_race_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_close_race_LDADD = $(LDADD) -lpthread
+gem_ctx_freq_LDADD = $(LDADD) $(top_builddir)/lib/libigt_perf.la
 gem_ctx_thrash_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_ctx_thrash_LDADD = $(LDADD) -lpthread
 gem_exec_parallel_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index 4a81ac4a..3d079c42 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -58,6 +58,7 @@ TESTS_progs = \
 	gem_ctx_bad_exec \
 	gem_ctx_create \
 	gem_ctx_exec \
+	gem_ctx_freq \
 	gem_ctx_isolation \
 	gem_ctx_param \
 	gem_ctx_switch \
diff --git a/tests/gem_ctx_freq.c b/tests/gem_ctx_freq.c
new file mode 100644
index 00000000..f3cee838
--- /dev/null
+++ b/tests/gem_ctx_freq.c
@@ -0,0 +1,648 @@
+/*
+ * 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 <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 <time.h>
+
+#include "igt.h"
+#include "igt_perf.h"
+
+#define LOCAL_CONTEXT_PARAM_FREQUENCY 7
+
+#define SAMPLE_PERIOD (USEC_PER_SEC / 10)
+
+static int __set_freq(int fd, uint32_t ctx, uint32_t min, uint32_t max)
+{
+	struct drm_i915_gem_context_param param = {
+		.ctx_id = ctx,
+		.param = LOCAL_CONTEXT_PARAM_FREQUENCY,
+		.value = (uint64_t)max << 32 | min,
+	};
+
+	return __gem_context_set_param(fd, &param);
+}
+
+static void set_freq(int fd, uint32_t ctx, uint32_t min, uint32_t max)
+{
+	igt_assert_eq(__set_freq(fd, ctx, min, max), 0);
+}
+
+static void get_freq(int fd, uint32_t ctx, uint32_t *min, uint32_t *max)
+{
+	struct drm_i915_gem_context_param param = {
+		.ctx_id = ctx,
+		.param = LOCAL_CONTEXT_PARAM_FREQUENCY,
+	};
+
+	gem_context_get_param(fd, &param);
+
+	*min = param.value & 0xffffffff;
+	*max = param.value >> 32;
+}
+
+static double measure_frequency(int pmu, int period_us)
+{
+	uint64_t data[2];
+	uint64_t d_t, d_v;
+
+	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 * 1e9 / d_t;
+}
+
+static void single(int fd, const struct intel_execution_engine *e)
+{
+#define N_STEPS 10
+	const unsigned int engine = e->exec_id | e->flags;
+	uint32_t ctx = gem_context_create(fd);
+	uint32_t min, max;
+	double measured;
+	igt_spin_t *spin;
+	int pmu;
+
+	get_freq(fd, ctx, &min, &max);
+	igt_info("Min freq: %dMHz; Max freq: %dMHz\n", min, max);
+
+	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
+	igt_require(pmu >= 0);
+
+	for (int step = 0; step <= 2*N_STEPS; step++) {
+		int frac = step > N_STEPS ? 2*N_STEPS - step : step;
+		uint32_t freq = min + (max - min) * frac / N_STEPS;
+		uint32_t cur, discard;
+
+		set_freq(fd, ctx, freq, freq);
+		get_freq(fd, ctx, &cur, &discard);
+
+		gem_quiescent_gpu(fd);
+		spin = __igt_spin_batch_new(fd, ctx, engine, 0);
+		usleep(10000);
+
+		measured = measure_frequency(pmu, SAMPLE_PERIOD);
+		igt_debugfs_dump(fd, "i915_rps_boost_info");
+
+		igt_spin_batch_free(fd, spin);
+		igt_info("%s(single): Measured %.1fMHz, expected %dMhz\n",
+			 e->name, measured, cur);
+		igt_assert(measured > cur - 100 && measured < cur + 100);
+	}
+	gem_quiescent_gpu(fd);
+
+	close(pmu);
+	gem_context_destroy(fd, ctx);
+
+#undef N_STEPS
+}
+
+static void continuous(int fd, const struct intel_execution_engine *e)
+{
+#define N_STEPS 10
+	const unsigned int engine = e->exec_id | e->flags;
+	uint32_t ctx = gem_context_create(fd);
+	uint32_t min, max;
+	double measured;
+	igt_spin_t *spin;
+	int pmu;
+
+	get_freq(fd, ctx, &min, &max);
+	igt_info("Min freq: %dMHz; Max freq: %dMHz\n", min, max);
+
+	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
+	igt_require(pmu >= 0);
+
+	gem_quiescent_gpu(fd);
+	spin = __igt_spin_batch_new(fd, ctx, engine, 0);
+	for (int step = 0; step <= 2*N_STEPS; step++) {
+		int frac = step > N_STEPS ? 2*N_STEPS - step : step;
+		uint32_t freq = min + (max - min) * frac / N_STEPS;
+		uint32_t cur, discard;
+		igt_spin_t *kick;
+
+		set_freq(fd, ctx, freq, freq);
+		get_freq(fd, ctx, &cur, &discard);
+
+		/*
+		 * When requesting a new frequency on the currently
+		 * executing context, it does not take effect until the
+		 * next context switch. In this case, we trigger a lite
+		 * restore.
+		 */
+		kick = __igt_spin_batch_new(fd, ctx, engine, 0);
+		igt_spin_batch_free(fd, spin);
+		spin = kick;
+
+		usleep(10000);
+
+		measured = measure_frequency(pmu, SAMPLE_PERIOD);
+		igt_debugfs_dump(fd, "i915_rps_boost_info");
+
+		igt_info("%s(continuous): Measured %.1fMHz, expected %dMhz\n",
+			 e->name, measured, cur);
+		igt_assert(measured > cur - 100 && measured < cur + 100);
+	}
+	igt_spin_batch_free(fd, spin);
+	gem_quiescent_gpu(fd);
+
+	close(pmu);
+	gem_context_destroy(fd, ctx);
+#undef N_STEPS
+}
+
+static void inflight(int fd, const struct intel_execution_engine *e)
+{
+	const unsigned int engine = e->exec_id | e->flags;
+	uint32_t ctx, min, max, freq, discard;
+	double measured;
+	igt_spin_t *plug, *work[2];
+	int pmu;
+
+	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
+	igt_require(pmu >= 0);
+
+	ctx = gem_context_create(fd);
+	get_freq(fd, ctx, &min, &max);
+	set_freq(fd, ctx, min, min);
+
+	igt_info("Min freq: %dMHz; Max freq: %dMHz\n", min, max);
+
+	gem_quiescent_gpu(fd);
+	plug = igt_spin_batch_new(fd, ctx, engine, 0);
+	gem_context_destroy(fd, ctx);
+	for (int n = 0; n < 16; n++) {
+		struct drm_i915_gem_exec_object2 obj = {
+			.handle = plug->handle,
+		};
+		struct drm_i915_gem_execbuffer2 eb = {
+			.buffer_count = 1,
+			.buffers_ptr = to_user_pointer(&obj),
+			.flags = engine,
+			.rsvd1 = gem_context_create(fd),
+		};
+		set_freq(fd, eb.rsvd1, min, min);
+		gem_execbuf(fd, &eb);
+		gem_context_destroy(fd, eb.rsvd1);
+	}
+	measured = measure_frequency(pmu, SAMPLE_PERIOD);
+	igt_debugfs_dump(fd, "i915_rps_boost_info");
+	igt_info("%s(plug): Measured %.1fMHz, expected %dMhz\n",
+		 e->name, measured, min);
+	igt_assert(measured > min - 100 && measured < min + 100);
+
+	ctx = gem_context_create(fd);
+	set_freq(fd, ctx, max, max);
+	work[0] = __igt_spin_batch_new(fd, ctx, engine, 0);
+
+	/* work is now queued but not executing */
+	freq = (max + min) / 2;
+	set_freq(fd, ctx, freq, freq);
+	get_freq(fd, ctx, &freq, &discard);
+	gem_context_destroy(fd, ctx);
+
+	ctx = gem_context_create(fd);
+	set_freq(fd, ctx, max, max);
+	work[1] = __igt_spin_batch_new(fd, ctx, engine, 0);
+	gem_context_destroy(fd, ctx);
+
+	igt_spin_batch_end(plug);
+	do
+		usleep(10000);
+	while (gem_bo_busy(fd, plug->handle));
+	igt_spin_batch_free(fd, plug);
+
+	/* Now work will execute */
+	measured = measure_frequency(pmu, SAMPLE_PERIOD);
+	igt_debugfs_dump(fd, "i915_engine_info");
+	igt_debugfs_dump(fd, "i915_rps_boost_info");
+	igt_info("%s(work0): Measured %.1fMHz, expected %dMhz\n",
+		 e->name, measured, freq);
+	igt_assert(measured > freq - 100 && measured < freq + 100);
+
+	igt_spin_batch_end(work[0]);
+	do
+		usleep(10000);
+	while (gem_bo_busy(fd, work[0]->handle));
+	igt_spin_batch_free(fd, work[0]);
+
+	measured = measure_frequency(pmu, SAMPLE_PERIOD);
+	igt_debugfs_dump(fd, "i915_engine_info");
+	igt_debugfs_dump(fd, "i915_rps_boost_info");
+	igt_info("%s(work1): Measured %.1fMHz, expected %dMhz\n",
+		 e->name, measured, max);
+	igt_assert(measured > max - 100 && measured < max + 100);
+
+	igt_spin_batch_free(fd, work[1]);
+	close(pmu);
+	gem_quiescent_gpu(fd);
+}
+
+static void sandwich(int fd)
+{
+	uint32_t ctx = gem_context_create(fd);
+	unsigned int engine;
+	uint32_t min, max;
+	igt_spin_t *spin;
+	int pmu;
+
+	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
+	igt_require(pmu >= 0);
+
+	spin = igt_spin_batch_new(fd, ctx, 0, 0);
+	get_freq(fd, ctx, &min, &max);
+	set_freq(fd, ctx, min, min);
+	for_each_physical_engine(fd, engine) {
+		struct drm_i915_gem_exec_object2 obj = {
+			.handle = spin->handle,
+		};
+		struct drm_i915_gem_execbuffer2 eb = {
+			.buffer_count = 1,
+			.buffers_ptr = to_user_pointer(&obj),
+			.flags = engine,
+			.rsvd1 = ctx,
+		};
+		uint32_t cur, discard;
+		double measured;
+
+		min += 50;
+		if (min > max)
+			break;
+
+		set_freq(fd, ctx, min, min);
+		get_freq(fd, ctx, &cur, &discard);
+
+		gem_execbuf(fd, &eb);
+		usleep(10000);
+
+		measured = measure_frequency(pmu, SAMPLE_PERIOD);
+		igt_debugfs_dump(fd, "i915_rps_boost_info");
+
+		igt_info("Measured %.1fMHz, expected %dMhz\n", measured, cur);
+		igt_assert(measured > cur - 100 && measured < cur + 100);
+	}
+	igt_spin_batch_free(fd, spin);
+	gem_quiescent_gpu(fd);
+
+	gem_context_destroy(fd, ctx);
+	close(pmu);
+}
+
+static void pwm(int fd, unsigned int *engines, unsigned int nengine, int link)
+{
+	uint32_t ctx[nengine];
+
+	fcntl(link, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
+
+	for (unsigned int n = 0; n < nengine; n++)
+		ctx[n] = gem_context_create(fd);
+
+	do {
+		igt_spin_t *spin;
+		struct {
+			uint32_t engine;
+			uint32_t min;
+			uint32_t max;
+		} req;
+
+		while (read(link, &req, sizeof(req)) > 0) {
+			if ((req.engine | req.min | req.max) == 0)
+				goto out;
+
+			igt_assert(req.engine < nengine);
+			set_freq(fd, ctx[req.engine], req.min, req.max);
+		}
+
+		/* Create a 20% load using busy spinners */
+		spin = __igt_spin_batch_new(fd, ctx[0], engines[0], 0);
+		for (unsigned int n = 1; n < nengine; n++) {
+			struct drm_i915_gem_exec_object2 obj = {
+				.handle = spin->handle,
+			};
+			struct drm_i915_gem_execbuffer2 eb = {
+				.buffer_count = 1,
+				.buffers_ptr = to_user_pointer(&obj),
+				.flags = engines[n],
+				.rsvd1 = ctx[n],
+			};
+			gem_execbuf(fd, &eb);
+		}
+		usleep(100);
+		igt_spin_batch_end(spin);
+
+		do
+			usleep(10);
+		while (gem_bo_busy(fd, spin->handle));
+		igt_spin_batch_free(fd, spin);
+		usleep(400);
+	} while (1);
+
+out:
+	for (unsigned int n = 0; n < nengine; n++)
+		gem_context_destroy(fd, ctx[n]);
+}
+
+static void smoketest(int fd, int timeout)
+{
+	unsigned int engines[16];
+	unsigned int nengine;
+	unsigned int engine;
+	uint32_t min[16], max[16];
+	int pmu, link[2];
+
+	get_freq(fd, 0, &min[0], &max[0]);
+
+	nengine = 0;
+	for_each_physical_engine(fd, engine) {
+		if (nengine == ARRAY_SIZE(engines) - 1)
+			break;
+
+		min[nengine] = min[0];
+		max[nengine] = max[0];
+		engines[nengine] = engine;
+		nengine++;
+	}
+	igt_require(nengine);
+
+	igt_assert(pipe(link) == 0);
+	igt_fork(child, 1)
+		pwm(fd, engines, nengine, link[0]);
+	close(link[0]);
+
+	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
+	igt_require(pmu >= 0);
+
+	igt_until_timeout(timeout) {
+		struct {
+			uint32_t engine;
+			uint32_t min;
+			uint32_t max;
+		} req;
+		double measured;
+		uint32_t ctx;
+
+		req.engine = rand() % nengine;
+
+		ctx = gem_context_create(fd);
+		get_freq(fd, ctx, &req.min, &req.max);
+		req.min = rand() % (req.max - req.min) + req.min;
+		req.max = rand() % (req.max - req.min) + req.min;
+		set_freq(fd, ctx, req.min, req.max);
+		get_freq(fd, ctx, &req.min, &req.max);
+
+		igt_debug("Replacing (%d, %d) on engine %x with (%d, %d)\n",
+			  min[req.engine], max[req.engine], req.engine,
+			  req.min, req.max);
+		igt_assert(write(link[1], &req, sizeof(req)) == sizeof(req));
+		gem_context_destroy(fd, ctx);
+
+		min[req.engine] = req.min;
+		max[req.engine] = req.max;
+
+		for (unsigned int n = 0; n < nengine; n++) {
+			igt_debug("[%d]: [%d, %d]\n", n, min[n], max[n]);
+			if (min[n] < req.min)
+				req.min = min[n];
+			if (max[n] > req.max)
+				req.max = max[n];
+		}
+		igt_assert(req.max >= req.min);
+
+		usleep(50000);
+		measured = measure_frequency(pmu, SAMPLE_PERIOD);
+
+		if (measured <= req.min - 100 || measured >= req.max + 100)
+			igt_debugfs_dump(fd, "i915_rps_boost_info");
+		igt_info("Measured %.1fMHz, expected [%d, %d]Mhz\n",
+			 measured, req.min, req.max);
+		igt_assert(measured > req.min - 100 &&
+			   measured < req.max + 100);
+	}
+
+	do {
+		struct {
+			uint32_t engine;
+			uint32_t min;
+			uint32_t max;
+		} req = {};
+
+		write(link[1], &req, sizeof(req));
+		close(link[1]);
+	} while (0);
+	igt_waitchildren();
+	gem_quiescent_gpu(fd);
+
+	close(pmu);
+}
+
+static void check_invalid(int fd, uint32_t ctx, uint32_t min, uint32_t max)
+{
+	const struct test {
+		uint32_t min, max;
+	} tests[] = {
+		{ min - 50, max - 50 },
+		{ min - 50, max },
+		{ min - 50, max + 50 },
+		{ min, max + 50 },
+		{ min + 50, max + 50 },
+
+		{ min - 50, min - 50 },
+
+		{ min - 50, min },
+		{ min + 50, min },
+		{ min, min - 50 },
+
+		{ max + 50, max },
+		{ max, max + 50 },
+		{ max, max - 50 },
+
+		{ max + 50, max + 50 },
+
+		{}
+	};
+
+	for (const struct test *t = tests; t->min | t->max; t++) {
+		uint32_t cur_min, cur_max;
+
+		igt_assert_f(__set_freq(fd, ctx, t->min, t->max) == -EINVAL,
+			     "Failed to reject invalid [%d, %d] (valid range [%d, %d]) on context %d\n",
+			     t->min, t->max, min, max, ctx);
+
+		get_freq(fd, 0, &cur_min, &cur_max);
+		igt_assert_eq(cur_min, min);
+		igt_assert_eq(cur_max, max);
+	}
+}
+
+static void invalid_param(int fd)
+{
+	uint32_t min, max, ctx;
+
+	get_freq(fd, 0, &min, &max);
+
+	check_invalid(fd, 0, min, max);
+
+	ctx = gem_context_create(fd);
+	check_invalid(fd, ctx, min, max);
+	gem_context_destroy(fd, ctx);
+}
+
+static void idempotent(int fd)
+{
+	uint32_t min, max;
+	uint32_t cur_min, cur_max;
+
+	get_freq(fd, 0, &min, &max);
+
+	set_freq(fd, 0, max, max);
+	get_freq(fd, 0, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, max);
+	igt_assert_eq(cur_max, max);
+
+	set_freq(fd, 0, min, min);
+	get_freq(fd, 0, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, min);
+
+	set_freq(fd, 0, min, max);
+	get_freq(fd, 0, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+}
+
+static void independent(int fd)
+{
+	uint32_t min, max;
+	uint32_t cur_min, cur_max;
+	uint32_t ctx[2];
+
+	get_freq(fd, 0, &min, &max);
+
+	set_freq(fd, 0, max, max);
+	ctx[0] = gem_context_create(fd);
+	get_freq(fd, ctx[0], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+
+	set_freq(fd, 0, min, min);
+	get_freq(fd, ctx[0], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+
+	ctx[1] = gem_context_create(fd);
+	get_freq(fd, ctx[1], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+
+	set_freq(fd, ctx[1], max, max);
+	get_freq(fd, ctx[0], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+
+	get_freq(fd, 0, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, min);
+
+	get_freq(fd, ctx[1], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, max);
+	igt_assert_eq(cur_max, max);
+	gem_context_destroy(fd, ctx[1]);
+
+	get_freq(fd, ctx[0], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+	gem_context_destroy(fd, ctx[0]);
+}
+
+static bool has_ctx_freq(int fd)
+{
+	struct drm_i915_gem_context_param param = {
+		.param = LOCAL_CONTEXT_PARAM_FREQUENCY,
+	};
+
+	return __gem_context_get_param(fd, &param) == 0;
+}
+
+igt_main
+{
+	const struct intel_execution_engine *e;
+	int fd = -1;
+
+	igt_fixture {
+		fd = drm_open_driver(DRIVER_INTEL);
+		igt_require_gem(fd);
+
+		igt_require(has_ctx_freq(fd));
+	}
+
+	igt_subtest("invalid")
+		invalid_param(fd);
+
+	igt_subtest("idempotent")
+		idempotent(fd);
+
+	igt_subtest("independent")
+		independent(fd);
+
+	igt_skip_on_simulation();
+
+	for (e = intel_execution_engines; e->name; e++) {
+		if (e->exec_id == 0)
+			continue;
+
+		igt_subtest_group {
+			igt_fixture {
+				igt_require(gem_ring_has_physical_engine(fd, e->exec_id | e->flags));
+			}
+
+			igt_subtest_f("%s-single", e->name)
+				single(fd, e);
+			igt_subtest_f("%s-continuous", e->name)
+				continuous(fd, e);
+			igt_subtest_f("%s-inflight", e->name)
+				inflight(fd, e);
+		}
+	}
+
+	igt_subtest("sandwich")
+		sandwich(fd);
+
+	igt_subtest("smoketest")
+		smoketest(fd, 20);
+}
diff --git a/tests/meson.build b/tests/meson.build
index 6e776bb7..fa5e3169 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -36,6 +36,7 @@ test_progs = [
 	'gem_ctx_create',
 	'gem_ctx_exec',
 	'gem_ctx_isolation',
+	'gem_ctx_freq',
 	'gem_ctx_param',
 	'gem_ctx_switch',
 	'gem_ctx_thrash',
-- 
2.16.2

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

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

* Re: [PATCH igt] igt: Add gem_ctx_freq to exercise requesting freq on a ctx
  2018-03-09  0:45 ` Antonio Argenziano
@ 2018-03-09  1:03   ` Chris Wilson
  2018-03-09 19:15     ` Antonio Argenziano
  0 siblings, 1 reply; 36+ messages in thread
From: Chris Wilson @ 2018-03-09  1:03 UTC (permalink / raw)
  To: Antonio Argenziano, intel-gfx; +Cc: igt-dev, Praveen

Quoting Antonio Argenziano (2018-03-09 00:45:42)
> 
> 
> On 08/03/18 09:13, Chris Wilson wrote:
> > Exercise some new API that allows applications to request that
> > individual contexts are executed within a desired frequency range.
> > 
> > v2: Split single/continuous set_freq subtests
> > 
> > Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> > Cc: Paneri, Praveen <praveen.paneri@intel.com>
> > Cc: Kamble, Sagar A <sagar.a.kamble@intel.com>
> > Cc: Antonio Argenziano <antonio.argenziano@intel.com>
> > ---
> >   tests/Makefile.am      |   1 +
> >   tests/Makefile.sources |   1 +
> >   tests/gem_ctx_freq.c   | 604 +++++++++++++++++++++++++++++++++++++++++++++++++
> >   tests/meson.build      |   1 +
> >   4 files changed, 607 insertions(+)
> >   create mode 100644 tests/gem_ctx_freq.c
> > 
> 
> > +static void single(int fd, const struct intel_execution_engine *e)
> > +{
> > +     const unsigned int engine = e->exec_id | e->flags;
> > +     uint32_t ctx = gem_context_create(fd);
> > +     uint32_t min, max;
> > +     double measured;
> > +     igt_spin_t *spin;
> > +     int pmu;
> > +
> > +     get_freq(fd, ctx, &min, &max);
> > +     igt_info("Min freq: %dMHz; Max freq: %dMHz\n", min, max);
> > +
> > +     pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
> > +     igt_require(pmu >= 0);
> > +
> > +     for (uint32_t freq = min + 50; freq <= max; freq += 100) {
> 
> Although it is done in the smoke test, it would be interesting if freq's 
> values were a bit randomized.

It was intentionally increment only originally because the
implementation was fast to upclock and slow to downclock. Given the
challenge of the smoketest, I've shelved that laziness until it can pass
that not particularly challenging test.

I did have in mind doing an up/down ramp. We should also check that
changing min while keeping max fixed should not affect a 100% load like
the spinners.
 
> > +             uint32_t cur, discard;
> > +
> > +             set_freq(fd, ctx, freq, freq);
> > +             get_freq(fd, ctx, &cur, &discard);
> 
> igt_assert_eq(freq, cur)?

Not quite. The trick is that the interface is not strictly idempotent,
since we pass in MHz, the driver converts that into freq bins and spits
it back out to the nearest MHz. So cur is not strictly freq, it just
happens that 50MHz is the bin size on gen9.

The idea here is that we grab the adjusted freq from the driver to
validate with.

> > +
> > +             gem_quiescent_gpu(fd);
> > +             spin = __igt_spin_batch_new(fd, ctx, engine, 0);
> > +             usleep(10000);
> 
> I guess here we wait for the frequency changes to take effect, maybe a 
> small comment would help.

And for the batch to be submitted. Asynchronous interfaces make it hard
to pinpoint when things occur. We keep muttering about extending
igt_spin_t to have an option for wait-for-exec (basically have it write
out a dword that we can spin on)

usleep(10000); /* wait for execution + freq change */

> > +static void sandwich(int fd)
> > +{
> > +     uint32_t ctx = gem_context_create(fd);
> > +     unsigned int engine;
> > +     uint32_t min, max;
> > +     igt_spin_t *spin;
> > +     int pmu;
> > +
> > +     pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
> > +     igt_require(pmu >= 0);
> > +
> > +     spin = igt_spin_batch_new(fd, ctx, 0, 0);
> > +     get_freq(fd, ctx, &min, &max);
> > +     set_freq(fd, ctx, min, min);
> > +     for_each_physical_engine(fd, engine) {
> > +             struct drm_i915_gem_exec_object2 obj = {
> > +                     .handle = spin->handle,
> > +             };
> > +             struct drm_i915_gem_execbuffer2 eb = {
> > +                     .buffer_count = 1,
> > +                     .buffers_ptr = to_user_pointer(&obj),
> > +                     .flags = engine,
> > +                     .rsvd1 = ctx,
> > +             };
> > +             uint32_t cur, discard;
> > +             double measured;
> > +
> > +             min += 50;
> > +             if (min > max)
> > +                     break;
> > +
> > +             set_freq(fd, ctx, min, min);
> > +             get_freq(fd, ctx, &cur, &discard);
> > +
> > +             gem_execbuf(fd, &eb);
> > +             usleep(10000);
> > +
> > +             measured = measure_frequency(pmu, SAMPLE_PERIOD);
> > +             igt_debugfs_dump(fd, "i915_rps_boost_info");
> > +
> > +             igt_info("Measured %.1fMHz, expected %dMhz\n", measured, cur);
> > +             igt_assert(measured > cur - 100 && measured < cur + 100);
> 
> Does the frequency change after each execbuf?

Yes. This turned into just an exercise across engines, bumping the
desired freq. I wanted to try and validate the min/max bounds, but ran
into the challenge that the gpu is only running at the highest frequency
it can get because of the busy spinner :)

> > +static void smoketest(int fd, int timeout)
> > +{
> > +     unsigned int engines[16];
> 
> use a macro instead of magic number 16.

#define THIS_IS_FAR_MORE_MAGIC_THAN_A_MEANINGLESS_BARE_NUMBER_THAT_YOU_SHOULD_NOT_BE_READING_ANYTHING_INTO 16
/rant

> > +static void invalid_param(int fd)
> > +{
> 
> gem_ctx_param is going to be upset again pretty soon ;).

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

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

* Re: [PATCH igt] igt: Add gem_ctx_freq to exercise requesting freq on a ctx
  2018-03-08 17:13 [PATCH igt] " Chris Wilson
@ 2018-03-09  0:45 ` Antonio Argenziano
  2018-03-09  1:03   ` Chris Wilson
  2018-03-09 13:46 ` Chris Wilson
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 36+ messages in thread
From: Antonio Argenziano @ 2018-03-09  0:45 UTC (permalink / raw)
  To: Chris Wilson, intel-gfx; +Cc: igt-dev, Praveen



On 08/03/18 09:13, Chris Wilson wrote:
> Exercise some new API that allows applications to request that
> individual contexts are executed within a desired frequency range.
> 
> v2: Split single/continuous set_freq subtests
> 
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> Cc: Paneri, Praveen <praveen.paneri@intel.com>
> Cc: Kamble, Sagar A <sagar.a.kamble@intel.com>
> Cc: Antonio Argenziano <antonio.argenziano@intel.com>
> ---
>   tests/Makefile.am      |   1 +
>   tests/Makefile.sources |   1 +
>   tests/gem_ctx_freq.c   | 604 +++++++++++++++++++++++++++++++++++++++++++++++++
>   tests/meson.build      |   1 +
>   4 files changed, 607 insertions(+)
>   create mode 100644 tests/gem_ctx_freq.c
> 

> +static void single(int fd, const struct intel_execution_engine *e)
> +{
> +	const unsigned int engine = e->exec_id | e->flags;
> +	uint32_t ctx = gem_context_create(fd);
> +	uint32_t min, max;
> +	double measured;
> +	igt_spin_t *spin;
> +	int pmu;
> +
> +	get_freq(fd, ctx, &min, &max);
> +	igt_info("Min freq: %dMHz; Max freq: %dMHz\n", min, max);
> +
> +	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
> +	igt_require(pmu >= 0);
> +
> +	for (uint32_t freq = min + 50; freq <= max; freq += 100) {

Although it is done in the smoke test, it would be interesting if freq's 
values were a bit randomized.

> +		uint32_t cur, discard;
> +
> +		set_freq(fd, ctx, freq, freq);
> +		get_freq(fd, ctx, &cur, &discard);

igt_assert_eq(freq, cur)?

> +
> +		gem_quiescent_gpu(fd);
> +		spin = __igt_spin_batch_new(fd, ctx, engine, 0);
> +		usleep(10000);

I guess here we wait for the frequency changes to take effect, maybe a 
small comment would help.

> +
> +		measured = measure_frequency(pmu, SAMPLE_PERIOD);
> +		igt_debugfs_dump(fd, "i915_rps_boost_info");
> +
> +		igt_spin_batch_free(fd, spin);
> +		igt_info("%s(single): Measured %.1fMHz, expected %dMhz\n",
> +			 e->name, measured, cur);
> +		igt_assert(measured > cur - 100 && measured < cur + 100);
> +	}
> +	gem_quiescent_gpu(fd);
> +
> +	close(pmu);
> +	gem_context_destroy(fd, ctx);
> +}
> +

> +
> +static void sandwich(int fd)
> +{
> +	uint32_t ctx = gem_context_create(fd);
> +	unsigned int engine;
> +	uint32_t min, max;
> +	igt_spin_t *spin;
> +	int pmu;
> +
> +	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
> +	igt_require(pmu >= 0);
> +
> +	spin = igt_spin_batch_new(fd, ctx, 0, 0);
> +	get_freq(fd, ctx, &min, &max);
> +	set_freq(fd, ctx, min, min);
> +	for_each_physical_engine(fd, engine) {
> +		struct drm_i915_gem_exec_object2 obj = {
> +			.handle = spin->handle,
> +		};
> +		struct drm_i915_gem_execbuffer2 eb = {
> +			.buffer_count = 1,
> +			.buffers_ptr = to_user_pointer(&obj),
> +			.flags = engine,
> +			.rsvd1 = ctx,
> +		};
> +		uint32_t cur, discard;
> +		double measured;
> +
> +		min += 50;
> +		if (min > max)
> +			break;
> +
> +		set_freq(fd, ctx, min, min);
> +		get_freq(fd, ctx, &cur, &discard);
> +
> +		gem_execbuf(fd, &eb);
> +		usleep(10000);
> +
> +		measured = measure_frequency(pmu, SAMPLE_PERIOD);
> +		igt_debugfs_dump(fd, "i915_rps_boost_info");
> +
> +		igt_info("Measured %.1fMHz, expected %dMhz\n", measured, cur);
> +		igt_assert(measured > cur - 100 && measured < cur + 100);

Does the frequency change after each execbuf?

> +	}
> +	igt_spin_batch_free(fd, spin);
> +	gem_quiescent_gpu(fd);
> +
> +	gem_context_destroy(fd, ctx);
> +	close(pmu);
> +}
> +
> +static void pwm(int fd, unsigned int *engines, unsigned int nengine, int link)
> +{
> +	uint32_t ctx[nengine];
> +
> +	fcntl(link, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
> +
> +	for (unsigned int n = 0; n < nengine; n++)
> +		ctx[n] = gem_context_create(fd);
> +
> +	do {
> +		igt_spin_t *spin;
> +		struct {
> +			uint32_t engine;
> +			uint32_t min;
> +			uint32_t max;
> +		} req;
> +
> +		while (read(link, &req, sizeof(req)) > 0) {
> +			if ((req.engine | req.min | req.max) == 0)
> +				goto out;
> +
> +			igt_assert(req.engine < nengine);
> +			set_freq(fd, ctx[req.engine], req.min, req.max);
> +		}
> +
> +		/* Create a 20% load using busy spinners */
> +		spin = __igt_spin_batch_new(fd, ctx[0], engines[0], 0);
> +		for (unsigned int n = 1; n < nengine; n++) {
> +			struct drm_i915_gem_exec_object2 obj = {
> +				.handle = spin->handle,
> +			};
> +			struct drm_i915_gem_execbuffer2 eb = {
> +				.buffer_count = 1,
> +				.buffers_ptr = to_user_pointer(&obj),
> +				.flags = engines[n],
> +				.rsvd1 = ctx[n],
> +			};
> +			gem_execbuf(fd, &eb);
> +		}
> +		usleep(100);
> +		igt_spin_batch_end(spin);
> +
> +		do
> +			usleep(10);
> +		while (gem_bo_busy(fd, spin->handle));
> +		igt_spin_batch_free(fd, spin);
> +		usleep(400);
> +	} while (1);
> +
> +out:
> +	for (unsigned int n = 0; n < nengine; n++)
> +		gem_context_destroy(fd, ctx[n]);
> +}
> +
> +static void smoketest(int fd, int timeout)
> +{
> +	unsigned int engines[16];

use a macro instead of magic number 16.

> +	unsigned int nengine;
> +	unsigned int engine;
> +	uint32_t min[16], max[16];
> +	int pmu, link[2];
> +
> +	get_freq(fd, 0, &min[0], &max[0]);
> +
> +	nengine = 0;
> +	for_each_physical_engine(fd, engine) {
> +		if (nengine == ARRAY_SIZE(engines) - 1)
> +			break;
> +
> +		min[nengine] = min[0];
> +		max[nengine] = max[0];
> +		engines[nengine] = engine;
> +		nengine++;
> +	}
> +	igt_require(nengine);
> +
> +	igt_assert(pipe(link) == 0);
> +	igt_fork(child, 1)
> +		pwm(fd, engines, nengine, link[0]);
> +	close(link[0]);
> +
> +	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
> +	igt_require(pmu >= 0);
> +
> +	igt_until_timeout(timeout) {
> +		struct {
> +			uint32_t engine;
> +			uint32_t min;
> +			uint32_t max;
> +		} req;
> +		double measured;
> +		uint32_t ctx;
> +
> +		req.engine = rand() % nengine;
> +
> +		ctx = gem_context_create(fd);
> +		get_freq(fd, ctx, &req.min, &req.max);
> +		req.min = rand() % (req.max - req.min) + req.min;
> +		req.max = rand() % (req.max - req.min) + req.min;
> +		set_freq(fd, ctx, req.min, req.max);
> +		get_freq(fd, ctx, &req.min, &req.max);
> +
> +		igt_debug("Replacing (%d, %d) on engine %x with (%d, %d)\n",
> +			  min[req.engine], max[req.engine], req.engine,
> +			  req.min, req.max);
> +		igt_assert(write(link[1], &req, sizeof(req)) == sizeof(req));
> +		gem_context_destroy(fd, ctx);
> +
> +		min[req.engine] = req.min;
> +		max[req.engine] = req.max;
> +
> +		for (unsigned int n = 0; n < nengine; n++) {
> +			igt_debug("[%d]: [%d, %d]\n", n, min[n], max[n]);
> +			if (min[n] < req.min)
> +				req.min = min[n];
> +			if (max[n] > req.max)
> +				req.max = max[n];
> +		}
> +		igt_assert(req.max >= req.min);
> +
> +		usleep(50000);
> +		measured = measure_frequency(pmu, SAMPLE_PERIOD);
> +
> +		if (measured <= req.min - 100 || measured >= req.max + 100)
> +			igt_debugfs_dump(fd, "i915_rps_boost_info");
> +		igt_info("Measured %.1fMHz, expected [%d, %d]Mhz\n",
> +			 measured, req.min, req.max);
> +		igt_assert(measured > req.min - 100 &&
> +			   measured < req.max + 100);
> +	}
> +
> +	do {
> +		struct {
> +			uint32_t engine;
> +			uint32_t min;
> +			uint32_t max;
> +		} req = {};
> +
> +		write(link[1], &req, sizeof(req));
> +		close(link[1]);
> +	} while (0);
> +	igt_waitchildren();
> +	gem_quiescent_gpu(fd);
> +
> +	close(pmu);
> +}
> +
> +static void invalid_param(int fd)
> +{

gem_ctx_param is going to be upset again pretty soon ;).

> +	uint32_t min, max;
> +	uint32_t cur_min, cur_max;
> +
> +	get_freq(fd, 0, &min, &max);
> +
> +	igt_assert_eq(__set_freq(fd, 0, min - 50, max), -EINVAL);
> +	igt_assert_eq(__set_freq(fd, 0, min, max + 50), -EINVAL);

One more case is both are out of boundary.

igt_assert_eq(__set_freq(fd, 0, min - 50, max + 50), -EINVAL);

> +	igt_assert_eq(__set_freq(fd, 0, min + 50, min), -EINVAL);
> +	igt_assert_eq(__set_freq(fd, 0, max, max - 50), -EINVAL);
> +
> +	get_freq(fd, 0, &cur_min, &cur_max);
> +	igt_assert_eq(cur_min, min);
> +	igt_assert_eq(cur_max, max);

Check frequency didn't change after each ioctl.

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

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

* [PATCH igt] igt: Add gem_ctx_freq to exercise requesting freq on a ctx
@ 2018-03-08 17:13 Chris Wilson
  2018-03-09  0:45 ` Antonio Argenziano
                   ` (4 more replies)
  0 siblings, 5 replies; 36+ messages in thread
From: Chris Wilson @ 2018-03-08 17:13 UTC (permalink / raw)
  To: intel-gfx; +Cc: Paneri, Kamble, igt-dev, Praveen

Exercise some new API that allows applications to request that
individual contexts are executed within a desired frequency range.

v2: Split single/continuous set_freq subtests

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Paneri, Praveen <praveen.paneri@intel.com>
Cc: Kamble, Sagar A <sagar.a.kamble@intel.com>
Cc: Antonio Argenziano <antonio.argenziano@intel.com>
---
 tests/Makefile.am      |   1 +
 tests/Makefile.sources |   1 +
 tests/gem_ctx_freq.c   | 604 +++++++++++++++++++++++++++++++++++++++++++++++++
 tests/meson.build      |   1 +
 4 files changed, 607 insertions(+)
 create mode 100644 tests/gem_ctx_freq.c

diff --git a/tests/Makefile.am b/tests/Makefile.am
index dbc7be72..389f7fc7 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -104,6 +104,7 @@ drm_import_export_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 drm_import_export_LDADD = $(LDADD) -lpthread
 gem_close_race_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_close_race_LDADD = $(LDADD) -lpthread
+gem_ctx_freq_LDADD = $(LDADD) $(top_builddir)/lib/libigt_perf.la
 gem_ctx_thrash_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 gem_ctx_thrash_LDADD = $(LDADD) -lpthread
 gem_exec_parallel_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index 4a81ac4a..3d079c42 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -58,6 +58,7 @@ TESTS_progs = \
 	gem_ctx_bad_exec \
 	gem_ctx_create \
 	gem_ctx_exec \
+	gem_ctx_freq \
 	gem_ctx_isolation \
 	gem_ctx_param \
 	gem_ctx_switch \
diff --git a/tests/gem_ctx_freq.c b/tests/gem_ctx_freq.c
new file mode 100644
index 00000000..f7e79ac3
--- /dev/null
+++ b/tests/gem_ctx_freq.c
@@ -0,0 +1,604 @@
+/*
+ * 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 <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 <time.h>
+
+#include "igt.h"
+#include "igt_perf.h"
+
+#define LOCAL_CONTEXT_PARAM_FREQUENCY 8
+
+#define SAMPLE_PERIOD (USEC_PER_SEC / 10)
+
+static int __set_freq(int fd, uint32_t ctx, uint32_t min, uint32_t max)
+{
+	struct drm_i915_gem_context_param param = {
+		.ctx_id = ctx,
+		.param = LOCAL_CONTEXT_PARAM_FREQUENCY,
+		.value = (uint64_t)max << 32 | min,
+	};
+
+	return __gem_context_set_param(fd, &param);
+}
+
+static void set_freq(int fd, uint32_t ctx, uint32_t min, uint32_t max)
+{
+	igt_assert_eq(__set_freq(fd, ctx, min, max), 0);
+}
+
+static void get_freq(int fd, uint32_t ctx, uint32_t *min, uint32_t *max)
+{
+	struct drm_i915_gem_context_param param = {
+		.ctx_id = ctx,
+		.param = LOCAL_CONTEXT_PARAM_FREQUENCY,
+	};
+
+	gem_context_get_param(fd, &param);
+
+	*min = param.value & 0xffffffff;
+	*max = param.value >> 32;
+}
+
+static double measure_frequency(int pmu, int period_us)
+{
+	uint64_t data[2];
+	uint64_t d_t, d_v;
+
+	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 * 1e9 / d_t;
+}
+
+static void single(int fd, const struct intel_execution_engine *e)
+{
+	const unsigned int engine = e->exec_id | e->flags;
+	uint32_t ctx = gem_context_create(fd);
+	uint32_t min, max;
+	double measured;
+	igt_spin_t *spin;
+	int pmu;
+
+	get_freq(fd, ctx, &min, &max);
+	igt_info("Min freq: %dMHz; Max freq: %dMHz\n", min, max);
+
+	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
+	igt_require(pmu >= 0);
+
+	for (uint32_t freq = min + 50; freq <= max; freq += 100) {
+		uint32_t cur, discard;
+
+		set_freq(fd, ctx, freq, freq);
+		get_freq(fd, ctx, &cur, &discard);
+
+		gem_quiescent_gpu(fd);
+		spin = __igt_spin_batch_new(fd, ctx, engine, 0);
+		usleep(10000);
+
+		measured = measure_frequency(pmu, SAMPLE_PERIOD);
+		igt_debugfs_dump(fd, "i915_rps_boost_info");
+
+		igt_spin_batch_free(fd, spin);
+		igt_info("%s(single): Measured %.1fMHz, expected %dMhz\n",
+			 e->name, measured, cur);
+		igt_assert(measured > cur - 100 && measured < cur + 100);
+	}
+	gem_quiescent_gpu(fd);
+
+	close(pmu);
+	gem_context_destroy(fd, ctx);
+}
+
+static void continuous(int fd, const struct intel_execution_engine *e)
+{
+	const unsigned int engine = e->exec_id | e->flags;
+	uint32_t ctx = gem_context_create(fd);
+	uint32_t min, max;
+	double measured;
+	igt_spin_t *spin;
+	int pmu;
+
+	get_freq(fd, ctx, &min, &max);
+	igt_info("Min freq: %dMHz; Max freq: %dMHz\n", min, max);
+
+	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
+	igt_require(pmu >= 0);
+
+	gem_quiescent_gpu(fd);
+	spin = __igt_spin_batch_new(fd, ctx, engine, 0);
+	for (uint32_t freq = min + 50; freq <= max; freq += 100) {
+		uint32_t cur, discard;
+		igt_spin_t *kick;
+
+		set_freq(fd, ctx, freq, freq);
+		get_freq(fd, ctx, &cur, &discard);
+
+		/*
+		 * When requesting a new frequency on the currently
+		 * executing context, it does not take effect until the
+		 * next context switch. In this case, we trigger a lite
+		 * restore.
+		 */
+		kick = __igt_spin_batch_new(fd, ctx, engine, 0);
+		igt_spin_batch_free(fd, spin);
+		spin = kick;
+
+		usleep(10000);
+
+		measured = measure_frequency(pmu, SAMPLE_PERIOD);
+		igt_debugfs_dump(fd, "i915_rps_boost_info");
+
+		igt_info("%s(continuous): Measured %.1fMHz, expected %dMhz\n",
+			 e->name, measured, cur);
+		igt_assert(measured > cur - 100 && measured < cur + 100);
+	}
+	igt_spin_batch_free(fd, spin);
+	gem_quiescent_gpu(fd);
+
+	close(pmu);
+	gem_context_destroy(fd, ctx);
+}
+
+static void inflight(int fd, const struct intel_execution_engine *e)
+{
+	const unsigned int engine = e->exec_id | e->flags;
+	uint32_t ctx, min, max, freq, discard;
+	double measured;
+	igt_spin_t *plug, *work[2];
+	int pmu;
+
+	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
+	igt_require(pmu >= 0);
+
+	ctx = gem_context_create(fd);
+	get_freq(fd, ctx, &min, &max);
+	set_freq(fd, ctx, min, min);
+
+	igt_info("Min freq: %dMHz; Max freq: %dMHz\n", min, max);
+
+	gem_quiescent_gpu(fd);
+	plug = igt_spin_batch_new(fd, ctx, engine, 0);
+	gem_context_destroy(fd, ctx);
+	for (int n = 0; n < 16; n++) {
+		struct drm_i915_gem_exec_object2 obj = {
+			.handle = plug->handle,
+		};
+		struct drm_i915_gem_execbuffer2 eb = {
+			.buffer_count = 1,
+			.buffers_ptr = to_user_pointer(&obj),
+			.flags = engine,
+			.rsvd1 = gem_context_create(fd),
+		};
+		set_freq(fd, eb.rsvd1, min, min);
+		gem_execbuf(fd, &eb);
+		gem_context_destroy(fd, eb.rsvd1);
+	}
+	measured = measure_frequency(pmu, SAMPLE_PERIOD);
+	igt_debugfs_dump(fd, "i915_rps_boost_info");
+	igt_info("%s(plug): Measured %.1fMHz, expected %dMhz\n",
+		 e->name, measured, min);
+	igt_assert(measured > min - 100 && measured < min + 100);
+
+	ctx = gem_context_create(fd);
+	set_freq(fd, ctx, max, max);
+	work[0] = __igt_spin_batch_new(fd, ctx, engine, 0);
+
+	/* work is now queued but not executing */
+	freq = (max + min) / 2;
+	set_freq(fd, ctx, freq, freq);
+	get_freq(fd, ctx, &freq, &discard);
+	gem_context_destroy(fd, ctx);
+
+	ctx = gem_context_create(fd);
+	set_freq(fd, ctx, max, max);
+	work[1] = __igt_spin_batch_new(fd, ctx, engine, 0);
+	gem_context_destroy(fd, ctx);
+
+	igt_spin_batch_end(plug);
+	do
+		usleep(10000);
+	while (gem_bo_busy(fd, plug->handle));
+	igt_spin_batch_free(fd, plug);
+
+	/* Now work will execute */
+	measured = measure_frequency(pmu, SAMPLE_PERIOD);
+	igt_debugfs_dump(fd, "i915_engine_info");
+	igt_debugfs_dump(fd, "i915_rps_boost_info");
+	igt_info("%s(work0): Measured %.1fMHz, expected %dMhz\n",
+		 e->name, measured, freq);
+	igt_assert(measured > freq - 100 && measured < freq + 100);
+
+	igt_spin_batch_end(work[0]);
+	do
+		usleep(10000);
+	while (gem_bo_busy(fd, work[0]->handle));
+	igt_spin_batch_free(fd, work[0]);
+
+	measured = measure_frequency(pmu, SAMPLE_PERIOD);
+	igt_debugfs_dump(fd, "i915_engine_info");
+	igt_debugfs_dump(fd, "i915_rps_boost_info");
+	igt_info("%s(work1): Measured %.1fMHz, expected %dMhz\n",
+		 e->name, measured, max);
+	igt_assert(measured > max - 100 && measured < max + 100);
+
+	igt_spin_batch_free(fd, work[1]);
+	close(pmu);
+	gem_quiescent_gpu(fd);
+}
+
+static void sandwich(int fd)
+{
+	uint32_t ctx = gem_context_create(fd);
+	unsigned int engine;
+	uint32_t min, max;
+	igt_spin_t *spin;
+	int pmu;
+
+	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
+	igt_require(pmu >= 0);
+
+	spin = igt_spin_batch_new(fd, ctx, 0, 0);
+	get_freq(fd, ctx, &min, &max);
+	set_freq(fd, ctx, min, min);
+	for_each_physical_engine(fd, engine) {
+		struct drm_i915_gem_exec_object2 obj = {
+			.handle = spin->handle,
+		};
+		struct drm_i915_gem_execbuffer2 eb = {
+			.buffer_count = 1,
+			.buffers_ptr = to_user_pointer(&obj),
+			.flags = engine,
+			.rsvd1 = ctx,
+		};
+		uint32_t cur, discard;
+		double measured;
+
+		min += 50;
+		if (min > max)
+			break;
+
+		set_freq(fd, ctx, min, min);
+		get_freq(fd, ctx, &cur, &discard);
+
+		gem_execbuf(fd, &eb);
+		usleep(10000);
+
+		measured = measure_frequency(pmu, SAMPLE_PERIOD);
+		igt_debugfs_dump(fd, "i915_rps_boost_info");
+
+		igt_info("Measured %.1fMHz, expected %dMhz\n", measured, cur);
+		igt_assert(measured > cur - 100 && measured < cur + 100);
+	}
+	igt_spin_batch_free(fd, spin);
+	gem_quiescent_gpu(fd);
+
+	gem_context_destroy(fd, ctx);
+	close(pmu);
+}
+
+static void pwm(int fd, unsigned int *engines, unsigned int nengine, int link)
+{
+	uint32_t ctx[nengine];
+
+	fcntl(link, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
+
+	for (unsigned int n = 0; n < nengine; n++)
+		ctx[n] = gem_context_create(fd);
+
+	do {
+		igt_spin_t *spin;
+		struct {
+			uint32_t engine;
+			uint32_t min;
+			uint32_t max;
+		} req;
+
+		while (read(link, &req, sizeof(req)) > 0) {
+			if ((req.engine | req.min | req.max) == 0)
+				goto out;
+
+			igt_assert(req.engine < nengine);
+			set_freq(fd, ctx[req.engine], req.min, req.max);
+		}
+
+		/* Create a 20% load using busy spinners */
+		spin = __igt_spin_batch_new(fd, ctx[0], engines[0], 0);
+		for (unsigned int n = 1; n < nengine; n++) {
+			struct drm_i915_gem_exec_object2 obj = {
+				.handle = spin->handle,
+			};
+			struct drm_i915_gem_execbuffer2 eb = {
+				.buffer_count = 1,
+				.buffers_ptr = to_user_pointer(&obj),
+				.flags = engines[n],
+				.rsvd1 = ctx[n],
+			};
+			gem_execbuf(fd, &eb);
+		}
+		usleep(100);
+		igt_spin_batch_end(spin);
+
+		do
+			usleep(10);
+		while (gem_bo_busy(fd, spin->handle));
+		igt_spin_batch_free(fd, spin);
+		usleep(400);
+	} while (1);
+
+out:
+	for (unsigned int n = 0; n < nengine; n++)
+		gem_context_destroy(fd, ctx[n]);
+}
+
+static void smoketest(int fd, int timeout)
+{
+	unsigned int engines[16];
+	unsigned int nengine;
+	unsigned int engine;
+	uint32_t min[16], max[16];
+	int pmu, link[2];
+
+	get_freq(fd, 0, &min[0], &max[0]);
+
+	nengine = 0;
+	for_each_physical_engine(fd, engine) {
+		if (nengine == ARRAY_SIZE(engines) - 1)
+			break;
+
+		min[nengine] = min[0];
+		max[nengine] = max[0];
+		engines[nengine] = engine;
+		nengine++;
+	}
+	igt_require(nengine);
+
+	igt_assert(pipe(link) == 0);
+	igt_fork(child, 1)
+		pwm(fd, engines, nengine, link[0]);
+	close(link[0]);
+
+	pmu = perf_i915_open(I915_PMU_REQUESTED_FREQUENCY);
+	igt_require(pmu >= 0);
+
+	igt_until_timeout(timeout) {
+		struct {
+			uint32_t engine;
+			uint32_t min;
+			uint32_t max;
+		} req;
+		double measured;
+		uint32_t ctx;
+
+		req.engine = rand() % nengine;
+
+		ctx = gem_context_create(fd);
+		get_freq(fd, ctx, &req.min, &req.max);
+		req.min = rand() % (req.max - req.min) + req.min;
+		req.max = rand() % (req.max - req.min) + req.min;
+		set_freq(fd, ctx, req.min, req.max);
+		get_freq(fd, ctx, &req.min, &req.max);
+
+		igt_debug("Replacing (%d, %d) on engine %x with (%d, %d)\n",
+			  min[req.engine], max[req.engine], req.engine,
+			  req.min, req.max);
+		igt_assert(write(link[1], &req, sizeof(req)) == sizeof(req));
+		gem_context_destroy(fd, ctx);
+
+		min[req.engine] = req.min;
+		max[req.engine] = req.max;
+
+		for (unsigned int n = 0; n < nengine; n++) {
+			igt_debug("[%d]: [%d, %d]\n", n, min[n], max[n]);
+			if (min[n] < req.min)
+				req.min = min[n];
+			if (max[n] > req.max)
+				req.max = max[n];
+		}
+		igt_assert(req.max >= req.min);
+
+		usleep(50000);
+		measured = measure_frequency(pmu, SAMPLE_PERIOD);
+
+		if (measured <= req.min - 100 || measured >= req.max + 100)
+			igt_debugfs_dump(fd, "i915_rps_boost_info");
+		igt_info("Measured %.1fMHz, expected [%d, %d]Mhz\n",
+			 measured, req.min, req.max);
+		igt_assert(measured > req.min - 100 &&
+			   measured < req.max + 100);
+	}
+
+	do {
+		struct {
+			uint32_t engine;
+			uint32_t min;
+			uint32_t max;
+		} req = {};
+
+		write(link[1], &req, sizeof(req));
+		close(link[1]);
+	} while (0);
+	igt_waitchildren();
+	gem_quiescent_gpu(fd);
+
+	close(pmu);
+}
+
+static void invalid_param(int fd)
+{
+	uint32_t min, max;
+	uint32_t cur_min, cur_max;
+
+	get_freq(fd, 0, &min, &max);
+
+	igt_assert_eq(__set_freq(fd, 0, min - 50, max), -EINVAL);
+	igt_assert_eq(__set_freq(fd, 0, min, max + 50), -EINVAL);
+	igt_assert_eq(__set_freq(fd, 0, min + 50, min), -EINVAL);
+	igt_assert_eq(__set_freq(fd, 0, max, max - 50), -EINVAL);
+
+	get_freq(fd, 0, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+}
+
+static void idempotent(int fd)
+{
+	uint32_t min, max;
+	uint32_t cur_min, cur_max;
+
+	get_freq(fd, 0, &min, &max);
+
+	set_freq(fd, 0, max, max);
+	get_freq(fd, 0, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, max);
+	igt_assert_eq(cur_max, max);
+
+	set_freq(fd, 0, min, min);
+	get_freq(fd, 0, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, min);
+
+	set_freq(fd, 0, min, max);
+	get_freq(fd, 0, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+}
+
+static void independent(int fd)
+{
+	uint32_t min, max;
+	uint32_t cur_min, cur_max;
+	uint32_t ctx[2];
+
+	get_freq(fd, 0, &min, &max);
+
+	set_freq(fd, 0, max, max);
+	ctx[0] = gem_context_create(fd);
+	get_freq(fd, ctx[0], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+
+	set_freq(fd, 0, min, min);
+	get_freq(fd, ctx[0], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+
+	ctx[1] = gem_context_create(fd);
+	get_freq(fd, ctx[1], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+
+	set_freq(fd, ctx[1], max, max);
+	get_freq(fd, ctx[0], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+
+	get_freq(fd, 0, &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, min);
+
+	get_freq(fd, ctx[1], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, max);
+	igt_assert_eq(cur_max, max);
+	gem_context_destroy(fd, ctx[1]);
+
+	get_freq(fd, ctx[0], &cur_min, &cur_max);
+	igt_assert_eq(cur_min, min);
+	igt_assert_eq(cur_max, max);
+	gem_context_destroy(fd, ctx[0]);
+}
+
+static bool has_ctx_freq(int fd)
+{
+	struct drm_i915_gem_context_param param = {
+		.param = LOCAL_CONTEXT_PARAM_FREQUENCY,
+	};
+
+	return __gem_context_get_param(fd, &param) == 0;
+}
+
+igt_main
+{
+	const struct intel_execution_engine *e;
+	int fd = -1;
+
+	igt_fixture {
+		fd = drm_open_driver(DRIVER_INTEL);
+		igt_require_gem(fd);
+
+		igt_require(has_ctx_freq(fd));
+	}
+
+	igt_subtest("invalid")
+		invalid_param(fd);
+
+	igt_subtest("idempotent")
+		idempotent(fd);
+
+	igt_subtest("independent")
+		independent(fd);
+
+	igt_skip_on_simulation();
+
+	for (e = intel_execution_engines; e->name; e++) {
+		if (e->exec_id == 0)
+			continue;
+
+		igt_subtest_group {
+			igt_fixture {
+				igt_require(gem_ring_has_physical_engine(fd, e->exec_id | e->flags));
+			}
+
+			igt_subtest_f("%s-single", e->name)
+				single(fd, e);
+			igt_subtest_f("%s-continuous", e->name)
+				continuous(fd, e);
+			igt_subtest_f("%s-inflight", e->name)
+				inflight(fd, e);
+		}
+	}
+
+	igt_subtest("sandwich")
+		sandwich(fd);
+
+	igt_subtest("smoketest")
+		smoketest(fd, 20);
+}
diff --git a/tests/meson.build b/tests/meson.build
index 58729231..f1271274 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -35,6 +35,7 @@ test_progs = [
 	'gem_ctx_bad_exec',
 	'gem_ctx_create',
 	'gem_ctx_exec',
+	'gem_ctx_freq',
 	'gem_ctx_param',
 	'gem_ctx_switch',
 	'gem_ctx_thrash',
-- 
2.16.2

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

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

end of thread, other threads:[~2018-03-14  9:49 UTC | newest]

Thread overview: 36+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-03-07 22:49 [PATCH igt] igt: Add gem_ctx_freq to exercise requesting freq on a ctx Chris Wilson
2018-03-07 22:49 ` [igt-dev] " Chris Wilson
2018-03-07 23:00 ` [igt-dev] ✗ Fi.CI.BAT: failure for " Patchwork
2018-03-08  0:13 ` [PATCH igt] " Chris Wilson
2018-03-08  0:13   ` [igt-dev] " Chris Wilson
2018-03-08  0:42 ` [igt-dev] ✓ Fi.CI.BAT: success for igt: Add gem_ctx_freq to exercise requesting freq on a ctx (rev2) Patchwork
2018-03-08  0:55 ` [PATCH igt] igt: Add gem_ctx_freq to exercise requesting freq on a ctx Antonio Argenziano
2018-03-08  0:55   ` [Intel-gfx] " Antonio Argenziano
2018-03-08  1:18   ` Chris Wilson
2018-03-08  1:18     ` [igt-dev] [Intel-gfx] " Chris Wilson
2018-03-08 17:33     ` Antonio Argenziano
2018-03-08 17:33       ` [igt-dev] [Intel-gfx] " Antonio Argenziano
2018-03-08 17:39       ` Chris Wilson
2018-03-08 17:39         ` [igt-dev] [Intel-gfx] " Chris Wilson
2018-03-08  1:49 ` [igt-dev] ✗ Fi.CI.IGT: failure for igt: Add gem_ctx_freq to exercise requesting freq on a ctx (rev2) Patchwork
2018-03-08  1:59 ` [PATCH igt] igt: Add gem_ctx_freq to exercise requesting freq on a ctx Chris Wilson
2018-03-08  2:26 ` ✓ Fi.CI.BAT: success for " Patchwork
2018-03-08  3:12 ` ✓ Fi.CI.IGT: " Patchwork
2018-03-08  9:02 ` [PATCH igt v2] " Chris Wilson
2018-03-08 17:13 [PATCH igt] " Chris Wilson
2018-03-09  0:45 ` Antonio Argenziano
2018-03-09  1:03   ` Chris Wilson
2018-03-09 19:15     ` Antonio Argenziano
2018-03-09 20:37       ` Chris Wilson
2018-03-09 13:46 ` Chris Wilson
2018-03-09 17:06   ` Tvrtko Ursulin
2018-03-09 17:24     ` Chris Wilson
2018-03-09 21:35 ` Chris Wilson
2018-03-12 21:13   ` Antonio Argenziano
2018-03-13 12:38   ` Sagar Arun Kamble
2018-03-13 12:50     ` Chris Wilson
2018-03-13 13:26 ` Chris Wilson
2018-03-13 13:58 ` Chris Wilson
2018-03-14  8:15   ` Sagar Arun Kamble
2018-03-14  9:03     ` Chris Wilson
2018-03-14  9:49       ` Sagar Arun Kamble

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.