* [igt-dev] [PATCH i-g-t] igt: Add gem_ctx_freq to exercise requesting freq on a ctx
@ 2018-03-16 11:06 Arkadiusz Hiler
2018-03-16 12:19 ` [igt-dev] ✓ Fi.CI.BAT: success for igt: Add gem_ctx_freq to exercise requesting freq on a ctx (rev7) Patchwork
` (2 more replies)
0 siblings, 3 replies; 7+ messages in thread
From: Arkadiusz Hiler @ 2018-03-16 11:06 UTC (permalink / raw)
To: igt-dev; +Cc: Praveen Paneri
From: Chris Wilson <chris@chris-wilson.co.uk>
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.
v9: Fix meson build. (A. Hiler)
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
Reviewed-by: Sagar Arun Kamble <sagar.a.kamble@intel.com>
Signed-off-by: Arkadiusz Hiler <arkadiusz.hiler@intel.com>
---
tests/Makefile.am | 1 +
tests/Makefile.sources | 1 +
tests/gem_ctx_freq.c | 837 +++++++++++++++++++++++++++++++++++++++++++++++++
tests/meson.build | 20 +-
4 files changed, 853 insertions(+), 6 deletions(-)
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, ¶m);
+}
+
+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, ¶m);
+
+ *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, ¶m) == 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..1901cb16 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -1,3 +1,9 @@
+# those will be linked with lib_igt_perf
+perf_test_progs = [
+ 'gem_ctx_freq',
+ 'perf_pmu',
+]
+
test_progs = [
'core_auth',
'core_get_client_auth',
@@ -288,12 +294,14 @@ foreach prog : test_progs
install : true)
endforeach
-test_executables += executable('perf_pmu', 'perf_pmu.c',
- dependencies : test_deps + [ lib_igt_perf ],
- install_dir : libexecdir,
- install_rpath : rpathdir,
- install : true)
-test_progs += 'perf_pmu'
+foreach prog : perf_test_progs
+ test_executables += executable(prog, prog + '.c',
+ dependencies : test_deps + [ lib_igt_perf ],
+ install_dir : libexecdir,
+ install_rpath : rpathdir,
+ install : true)
+ test_progs += prog
+endforeach
executable('testdisplay', ['testdisplay.c', 'testdisplay_hotplug.c'],
dependencies : test_deps,
--
2.14.3
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [igt-dev] ✓ Fi.CI.BAT: success for igt: Add gem_ctx_freq to exercise requesting freq on a ctx (rev7)
2018-03-16 11:06 [igt-dev] [PATCH i-g-t] igt: Add gem_ctx_freq to exercise requesting freq on a ctx Arkadiusz Hiler
@ 2018-03-16 12:19 ` Patchwork
2018-03-16 14:15 ` [igt-dev] ✓ Fi.CI.IGT: " Patchwork
2018-04-10 16:17 ` [igt-dev] [PATCH i-g-t] igt: Add gem_ctx_freq to exercise requesting freq on a ctx Tvrtko Ursulin
2 siblings, 0 replies; 7+ messages in thread
From: Patchwork @ 2018-03-16 12:19 UTC (permalink / raw)
To: Chris Wilson; +Cc: igt-dev
== Series Details ==
Series: igt: Add gem_ctx_freq to exercise requesting freq on a ctx (rev7)
URL : https://patchwork.freedesktop.org/series/39564/
State : success
== Summary ==
IGT patchset tested on top of latest successful build
2e92134b4e4f754239f9721b8781ce2fc0aab07c tests/kms_frontbuffer_tracking: Reduce fbc status spam, v2.
with latest DRM-Tip kernel build CI_DRM_3938
d635bfa5779f drm-tip: 2018y-03m-16d-11h-00m-29s UTC integration manifest
Testlist changes:
+igt@gem_ctx_freq@blt-continuous
+igt@gem_ctx_freq@blt-inflight
+igt@gem_ctx_freq@blt-single
+igt@gem_ctx_freq@blt-sysfs
+igt@gem_ctx_freq@bsd1-continuous
+igt@gem_ctx_freq@bsd1-inflight
+igt@gem_ctx_freq@bsd1-single
+igt@gem_ctx_freq@bsd1-sysfs
+igt@gem_ctx_freq@bsd2-continuous
+igt@gem_ctx_freq@bsd2-inflight
+igt@gem_ctx_freq@bsd2-single
+igt@gem_ctx_freq@bsd2-sysfs
+igt@gem_ctx_freq@bsd-continuous
+igt@gem_ctx_freq@bsd-inflight
+igt@gem_ctx_freq@bsd-single
+igt@gem_ctx_freq@bsd-sysfs
+igt@gem_ctx_freq@default-continuous
+igt@gem_ctx_freq@default-inflight
+igt@gem_ctx_freq@default-single
+igt@gem_ctx_freq@default-sysfs
+igt@gem_ctx_freq@idempotent
+igt@gem_ctx_freq@independent
+igt@gem_ctx_freq@invalid
+igt@gem_ctx_freq@range
+igt@gem_ctx_freq@render-continuous
+igt@gem_ctx_freq@render-inflight
+igt@gem_ctx_freq@render-single
+igt@gem_ctx_freq@render-sysfs
+igt@gem_ctx_freq@sandwich
+igt@gem_ctx_freq@smoketest
+igt@gem_ctx_freq@vebox-continuous
+igt@gem_ctx_freq@vebox-inflight
+igt@gem_ctx_freq@vebox-single
+igt@gem_ctx_freq@vebox-sysfs
---- Known issues:
Test gem_exec_suspend:
Subgroup basic-s3:
pass -> DMESG-WARN (fi-skl-guc) fdo#104108
Test kms_pipe_crc_basic:
Subgroup hang-read-crc-pipe-c:
pass -> FAIL (fi-skl-6700k2) fdo#103191
Subgroup suspend-read-crc-pipe-b:
pass -> INCOMPLETE (fi-bdw-5557u) fdo#104944
fdo#104108 https://bugs.freedesktop.org/show_bug.cgi?id=104108
fdo#103191 https://bugs.freedesktop.org/show_bug.cgi?id=103191
fdo#104944 https://bugs.freedesktop.org/show_bug.cgi?id=104944
fi-bdw-5557u total:242 pass:224 dwarn:0 dfail:0 fail:0 skip:17
fi-bdw-gvtdvm total:285 pass:261 dwarn:0 dfail:0 fail:0 skip:24 time:443s
fi-blb-e6850 total:285 pass:220 dwarn:1 dfail:0 fail:0 skip:64 time:383s
fi-bsw-n3050 total:285 pass:239 dwarn:0 dfail:0 fail:0 skip:46 time:543s
fi-bwr-2160 total:285 pass:180 dwarn:0 dfail:0 fail:0 skip:105 time:300s
fi-bxt-dsi total:285 pass:255 dwarn:0 dfail:0 fail:0 skip:30 time:515s
fi-bxt-j4205 total:285 pass:256 dwarn:0 dfail:0 fail:0 skip:29 time:512s
fi-byt-j1900 total:285 pass:250 dwarn:0 dfail:0 fail:0 skip:35 time:520s
fi-byt-n2820 total:285 pass:246 dwarn:0 dfail:0 fail:0 skip:39 time:505s
fi-cfl-8700k total:285 pass:257 dwarn:0 dfail:0 fail:0 skip:28 time:409s
fi-cfl-s2 total:285 pass:259 dwarn:0 dfail:0 fail:0 skip:26 time:582s
fi-cfl-u total:285 pass:259 dwarn:0 dfail:0 fail:0 skip:26 time:509s
fi-cnl-drrs total:285 pass:254 dwarn:3 dfail:0 fail:0 skip:28 time:514s
fi-elk-e7500 total:285 pass:225 dwarn:1 dfail:0 fail:0 skip:59 time:426s
fi-gdg-551 total:285 pass:176 dwarn:0 dfail:0 fail:1 skip:108 time:318s
fi-glk-1 total:285 pass:257 dwarn:0 dfail:0 fail:0 skip:28 time:534s
fi-hsw-4770 total:285 pass:258 dwarn:0 dfail:0 fail:0 skip:27 time:402s
fi-ilk-650 total:285 pass:225 dwarn:0 dfail:0 fail:0 skip:60 time:419s
fi-ivb-3520m total:285 pass:256 dwarn:0 dfail:0 fail:0 skip:29 time:461s
fi-ivb-3770 total:285 pass:252 dwarn:0 dfail:0 fail:0 skip:33 time:428s
fi-kbl-7500u total:285 pass:260 dwarn:1 dfail:0 fail:0 skip:24 time:475s
fi-kbl-7567u total:285 pass:265 dwarn:0 dfail:0 fail:0 skip:20 time:467s
fi-kbl-r total:285 pass:258 dwarn:0 dfail:0 fail:0 skip:27 time:514s
fi-pnv-d510 total:285 pass:219 dwarn:1 dfail:0 fail:0 skip:65 time:661s
fi-skl-6260u total:285 pass:265 dwarn:0 dfail:0 fail:0 skip:20 time:447s
fi-skl-6600u total:285 pass:258 dwarn:0 dfail:0 fail:0 skip:27 time:543s
fi-skl-6700hq total:285 pass:259 dwarn:0 dfail:0 fail:0 skip:26 time:541s
fi-skl-6700k2 total:285 pass:260 dwarn:0 dfail:0 fail:1 skip:24 time:496s
fi-skl-6770hq total:285 pass:265 dwarn:0 dfail:0 fail:0 skip:20 time:493s
fi-skl-guc total:285 pass:256 dwarn:1 dfail:0 fail:0 skip:28 time:427s
fi-skl-gvtdvm total:285 pass:262 dwarn:0 dfail:0 fail:0 skip:23 time:444s
fi-snb-2520m total:285 pass:245 dwarn:0 dfail:0 fail:0 skip:40 time:569s
fi-snb-2600 total:285 pass:245 dwarn:0 dfail:0 fail:0 skip:40 time:398s
== Logs ==
For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_1148/issues.html
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev
^ permalink raw reply [flat|nested] 7+ messages in thread
* [igt-dev] ✓ Fi.CI.IGT: success for igt: Add gem_ctx_freq to exercise requesting freq on a ctx (rev7)
2018-03-16 11:06 [igt-dev] [PATCH i-g-t] igt: Add gem_ctx_freq to exercise requesting freq on a ctx Arkadiusz Hiler
2018-03-16 12:19 ` [igt-dev] ✓ Fi.CI.BAT: success for igt: Add gem_ctx_freq to exercise requesting freq on a ctx (rev7) Patchwork
@ 2018-03-16 14:15 ` Patchwork
2018-04-10 16:17 ` [igt-dev] [PATCH i-g-t] igt: Add gem_ctx_freq to exercise requesting freq on a ctx Tvrtko Ursulin
2 siblings, 0 replies; 7+ messages in thread
From: Patchwork @ 2018-03-16 14:15 UTC (permalink / raw)
To: Chris Wilson; +Cc: igt-dev
== Series Details ==
Series: igt: Add gem_ctx_freq to exercise requesting freq on a ctx (rev7)
URL : https://patchwork.freedesktop.org/series/39564/
State : success
== Summary ==
---- Possible new issues:
Test gem_tiled_swapping:
Subgroup non-threaded:
skip -> PASS (shard-hsw)
skip -> PASS (shard-snb)
---- Known issues:
Test gem_exec_suspend:
Subgroup basic-s3:
skip -> PASS (shard-snb) fdo#104311
Test kms_atomic_transition:
Subgroup 1x-modeset-transitions:
pass -> FAIL (shard-apl) fdo#103207
Test kms_flip:
Subgroup 2x-plain-flip-ts-check:
pass -> FAIL (shard-hsw) fdo#100368
Test kms_plane_multiple:
Subgroup atomic-pipe-a-tiling-x:
fail -> PASS (shard-snb) fdo#103166
fdo#104311 https://bugs.freedesktop.org/show_bug.cgi?id=104311
fdo#103207 https://bugs.freedesktop.org/show_bug.cgi?id=103207
fdo#100368 https://bugs.freedesktop.org/show_bug.cgi?id=100368
fdo#103166 https://bugs.freedesktop.org/show_bug.cgi?id=103166
shard-apl total:3476 pass:1813 dwarn:1 dfail:0 fail:8 skip:1653 time:12968s
shard-hsw total:3476 pass:1767 dwarn:1 dfail:0 fail:2 skip:1705 time:11977s
shard-snb total:3476 pass:1356 dwarn:1 dfail:0 fail:3 skip:2115 time:7221s
Blacklisted hosts:
shard-kbl total:2202 pass:1223 dwarn:4 dfail:0 fail:6 skip:969 time:6119s
== Logs ==
For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_1148/shards.html
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [igt-dev] [PATCH i-g-t] igt: Add gem_ctx_freq to exercise requesting freq on a ctx
2018-03-16 11:06 [igt-dev] [PATCH i-g-t] igt: Add gem_ctx_freq to exercise requesting freq on a ctx Arkadiusz Hiler
2018-03-16 12:19 ` [igt-dev] ✓ Fi.CI.BAT: success for igt: Add gem_ctx_freq to exercise requesting freq on a ctx (rev7) Patchwork
2018-03-16 14:15 ` [igt-dev] ✓ Fi.CI.IGT: " Patchwork
@ 2018-04-10 16:17 ` Tvrtko Ursulin
2018-04-10 20:53 ` Chris Wilson
2 siblings, 1 reply; 7+ messages in thread
From: Tvrtko Ursulin @ 2018-04-10 16:17 UTC (permalink / raw)
To: Arkadiusz Hiler, igt-dev; +Cc: Praveen Paneri
On 16/03/2018 11:06, Arkadiusz Hiler wrote:
> From: Chris Wilson <chris@chris-wilson.co.uk>
>
> 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.
> v9: Fix meson build. (A. Hiler)
>
> 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
> Reviewed-by: Sagar Arun Kamble <sagar.a.kamble@intel.com>
> Signed-off-by: Arkadiusz Hiler <arkadiusz.hiler@intel.com>
> ---
> tests/Makefile.am | 1 +
> tests/Makefile.sources | 1 +
> tests/gem_ctx_freq.c | 837 +++++++++++++++++++++++++++++++++++++++++++++++++
> tests/meson.build | 20 +-
> 4 files changed, 853 insertions(+), 6 deletions(-)
> 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, ¶m);
> +}
> +
> +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, ¶m);
> +
> + *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);
> +}
Hopefully +/- 100 MHz survives on thermally challenged devices.
> +
> +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 BYT I guess.
> +
> + 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;
> + }
Wouldn't harm moving to helper whose name could avoid writing a comment,
like make_triangle_wave(&frequencies, N_STEPS, min, max)?
> +
> + 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);
Use polling spinner?
> +
> + 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);
Sad for the little triangle. :) Do you even need it (triangle) in the
first place then?
> + }
> + 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",
You used __func__ above, but I am not sure why even put the test name
here since it will be obvious from the overall log?
> + 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);
I would expect queued contexts would have different limits so here you
can check that the limit from the currently executing one remains in
force? Like it is I am not sure what it's testing?
> +
> + ctx = gem_context_create(fd);
> + set_freq(fd, ctx, max, max);
This line looks important for the test so needs a comment since it is
not obvious.
> + 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));
gem_sync wouldn't cut it? Something internal could fool us here as the
queued up batches proceed to start and complete?
Also braces please or it looks to weird.
> + igt_spin_batch_free(fd, plug);
> +
> + /* Now work will execute */
> + measured = measure_frequency(pmu, SAMPLE_PERIOD);
> + igt_debugfs_dump(fd, "i915_engine_info");
Need this?
> + 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);
Assert it matches sysfs values?
> +
> + 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;
Move to helper? But if my eyes are not fooling me looks the same
triangle wave so for simplicity sake use local array and the helper I
suggested earlier to fill it?
> + uint32_t cur, discard;
> +
> + for (int inner = 0; inner <= 2*N_STEPS; inner++) {
Rename inner and outer to global_freq and ctx_freq, or something?
> + 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);
Read-back ctx freq and check is correct?
> +
> + measured = measure_frequency(pmu, SAMPLE_PERIOD);
> + igt_debugfs_dump(fd, "i915_rps_boost_info");
> +
> + set_sysfs_freq(sys_min, sys_max);
Just so we do not exit with random min/max set?
> +
> + 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 */
What kind of overlap do you mean? Deliberate absence of gem_sync
probably not since that woldn't be completely making sure, and the
commend is also associated with min/max calc.
> + 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);
> + }
Hmm.. so engines run in parallel - but how come all can assert they got
exactly the limits they wanted? Surely someone must get a different
range depending on what's running on another engine?
If the given range is reflected in the get_freq query after setting, but
then why can't it race (be changed) while the child is executing? Or you
are counting on the sampling period to be short enough the effect of
that is lost in tolerance?
> +
> + 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);
Needs a comment - it's very unobvious what is the connection between drm
fd and a communication pipe to be used like this.
> +
> + 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));
This loop should be moved to a helper since it is repeated a few times.
(If it is required in this form.)
> + igt_spin_batch_free(fd, spin);
> + usleep(400);
100us/400us is I think too short if you want to get close to target
ratio. Will see later whether you actualy depend on it.
> + } 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);
Have bi-directional comms so you know when child has processed the
updated request?
> + measured = measure_frequency(pmu, SAMPLE_PERIOD);
> +
> + if (measured <= req.min - PMU_TOLERANCE ||
> + measured >= req.max + PMU_TOLERANCE)
Could move to helper, think I've seen it once before so far.
> + 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);
Okay so there isn't any purpose in 20% PWM? Apart that frequency should
travel towards min when test context is in the idle PWM half? So how
come that does not affect the measured values?
> + }
> +
> + do {
> + struct {
> + uint32_t engine;
> + uint32_t min;
> + uint32_t max;
> + } req = {};
3x local struct definition.
> +
> + 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)
fixed? By idempotent I tought it will be one which cannot be modified.
> +{
> + 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, ¶m) == 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..1901cb16 100644
> --- a/tests/meson.build
> +++ b/tests/meson.build
> @@ -1,3 +1,9 @@
> +# those will be linked with lib_igt_perf
> +perf_test_progs = [
> + 'gem_ctx_freq',
> + 'perf_pmu',
> +]
> +
> test_progs = [
> 'core_auth',
> 'core_get_client_auth',
> @@ -288,12 +294,14 @@ foreach prog : test_progs
> install : true)
> endforeach
>
> -test_executables += executable('perf_pmu', 'perf_pmu.c',
> - dependencies : test_deps + [ lib_igt_perf ],
> - install_dir : libexecdir,
> - install_rpath : rpathdir,
> - install : true)
> -test_progs += 'perf_pmu'
> +foreach prog : perf_test_progs
> + test_executables += executable(prog, prog + '.c',
> + dependencies : test_deps + [ lib_igt_perf ],
> + install_dir : libexecdir,
> + install_rpath : rpathdir,
> + install : true)
> + test_progs += prog
> +endforeach
>
> executable('testdisplay', ['testdisplay.c', 'testdisplay_hotplug.c'],
> dependencies : test_deps,
>
Regards,
Tvrtko
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [igt-dev] [PATCH i-g-t] igt: Add gem_ctx_freq to exercise requesting freq on a ctx
2018-04-10 16:17 ` [igt-dev] [PATCH i-g-t] igt: Add gem_ctx_freq to exercise requesting freq on a ctx Tvrtko Ursulin
@ 2018-04-10 20:53 ` Chris Wilson
2018-04-11 10:42 ` Tvrtko Ursulin
0 siblings, 1 reply; 7+ messages in thread
From: Chris Wilson @ 2018-04-10 20:53 UTC (permalink / raw)
To: Tvrtko Ursulin, Arkadiusz Hiler, igt-dev; +Cc: Praveen Paneri
Quoting Tvrtko Ursulin (2018-04-10 17:17:13)
>
> On 16/03/2018 11:06, Arkadiusz Hiler wrote:
> > From: Chris Wilson <chris@chris-wilson.co.uk>
> >
> > 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.
> > v9: Fix meson build. (A. Hiler)
> >
> > 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
> > Reviewed-by: Sagar Arun Kamble <sagar.a.kamble@intel.com>
> > Signed-off-by: Arkadiusz Hiler <arkadiusz.hiler@intel.com>
> > ---
> > tests/Makefile.am | 1 +
> > tests/Makefile.sources | 1 +
> > tests/gem_ctx_freq.c | 837 +++++++++++++++++++++++++++++++++++++++++++++++++
> > tests/meson.build | 20 +-
> > 4 files changed, 853 insertions(+), 6 deletions(-)
> > 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, ¶m);
> > +}
> > +
> > +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, ¶m);
> > +
> > + *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);
> > +}
>
> Hopefully +/- 100 MHz survives on thermally challenged devices.
On most, yes. But I don't think thermal tolerance accounts for all of
it. A large systematic error I fear. Fortunately the ranges are large
enough that even if we can't tell two bins apart, we usually can tell
the two end points apart.
> > +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 BYT I guess.
For shame, yes.
> > +
> > + 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;
> > + }
>
> Wouldn't harm moving to helper whose name could avoid writing a comment,
> like make_triangle_wave(&frequencies, N_STEPS, min, max)?
>
> > +
> > + 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);
>
> Use polling spinner?
>
> > +
> > + 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);
>
> Sad for the little triangle. :) Do you even need it (triangle) in the
> first place then?
The triangle is just so that we have a predictable loop before we
randomise. Hopefully catching any error in the easily reproducible pass.
>
> > + }
> > + 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",
>
> You used __func__ above, but I am not sure why even put the test name
> here since it will be obvious from the overall log?
It started out life as one bigger function with multiple phases. Still useful
enough to distinguish the different subtest output when run together.
>
> > + 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);
>
> I would expect queued contexts would have different limits so here you
> can check that the limit from the currently executing one remains in
> force? Like it is I am not sure what it's testing?
The challenge here is to detect that setting a frequency is done on a
lite-restore, i.e. that the updated context frequency is used for the
next batch even though the context is currently live.
>
> > +
> > + ctx = gem_context_create(fd);
> > + set_freq(fd, ctx, max, max);
>
> This line looks important for the test so needs a comment since it is
> not obvious.
>
> > + 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));
>
> gem_sync wouldn't cut it? Something internal could fool us here as the
> queued up batches proceed to start and complete?
gem_sync -> waitboost. I was writing this to avoid triggering
waitboosts, and also to cancel out unwanted boosts between loops.
> Also braces please or it looks to weird.
Bah.
>
> > + igt_spin_batch_free(fd, plug);
> > +
> > + /* Now work will execute */
> > + measured = measure_frequency(pmu, SAMPLE_PERIOD);
> > + igt_debugfs_dump(fd, "i915_engine_info");
>
> Need this?
Need it? Have you tried debugging it without it? :)
> > + 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);
>
> Assert it matches sysfs values?
It doesn't. The context itself starts at hw limits, as the sysfs limits
may change over time; and the whole shebang of limits is applied at the
end to decide who wins.
>
> > +
> > + 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;
>
> Move to helper? But if my eyes are not fooling me looks the same
> triangle wave so for simplicity sake use local array and the helper I
> suggested earlier to fill it?
>
> > + uint32_t cur, discard;
> > +
> > + for (int inner = 0; inner <= 2*N_STEPS; inner++) {
>
> Rename inner and outer to global_freq and ctx_freq, or something?
>
> > + 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);
>
> Read-back ctx freq and check is correct?
Define correct. We spent many test cycles to verify these are accurate
at the points we can decide they are. Duplicating that fuzzy logic here,
I did not want to do as it is not the test we are focusing on.
It just happens that those basic API tests are after the behaviour
tests. (Boring tests came after the tests to check the code was
behaving.)
> > +
> > + measured = measure_frequency(pmu, SAMPLE_PERIOD);
> > + igt_debugfs_dump(fd, "i915_rps_boost_info");
> > +
> > + set_sysfs_freq(sys_min, sys_max);
>
> Just so we do not exit with random min/max set?
Yes, as much as possible try to ensure we only assert and end the
subtest on known state.
>
> > +
> > + 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 */
>
> What kind of overlap do you mean? Deliberate absence of gem_sync
> probably not since that woldn't be completely making sure, and the
> commend is also associated with min/max calc.
It's talking about how the frequency ranges must overlap.
Otherwise we may end up with a fixed frequency (max(min)) that is
outside the bounds of a single engine, violating our asserts.
>
> > + 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);
> > + }
>
> Hmm.. so engines run in parallel - but how come all can assert they got
> exactly the limits they wanted? Surely someone must get a different
> range depending on what's running on another engine?
That's the setup with the overlapping frequency ranges, it ensures that
we can satisfy all the requests simultaneously. If the ranges are
disjoint, then at least one engine's request cannot be satisfied.
> If the given range is reflected in the get_freq query after setting, but
> then why can't it race (be changed) while the child is executing? Or you
> are counting on the sampling period to be short enough the effect of
> that is lost in tolerance?
Just the range it is allowed to change is within the request on each
engine.
> > +
> > + 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);
>
> Needs a comment - it's very unobvious what is the connection between drm
> fd and a communication pipe to be used like this.
Not even obvious to me. I think someone kept on switching variable
names around and didn't notice he left fd behind!
> > +
> > + 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));
>
> This loop should be moved to a helper since it is repeated a few times.
> (If it is required in this form.)
>
> > + igt_spin_batch_free(fd, spin);
> > + usleep(400);
>
> 100us/400us is I think too short if you want to get close to target
> ratio. Will see later whether you actualy depend on it.
The target ratio here is misleading, busyness is summed over engines, so
a 5x pulses have a weird effect. I've no idea what the load should look
like, the challenge is just to ensure that we do not have a 100% saturated
load and so allow the GPU to downclock. To allow downclocking we have
to be less than 65% busy (worst case). Other than by watching and
confirming we up/downclock i.e. the loading was good enough, I don't
think the pulses are the best method.
> > + } 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);
>
> Have bi-directional comms so you know when child has processed the
> updated request?
Not just updated request, but the kernel rps worker to do something.
A bit hazy.
> > + measured = measure_frequency(pmu, SAMPLE_PERIOD);
> > +
> > + if (measured <= req.min - PMU_TOLERANCE ||
> > + measured >= req.max + PMU_TOLERANCE)
>
> Could move to helper, think I've seen it once before so far.
The oft-repeated one is, this form iirc was used at most twice.
>
> > + 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);
>
> Okay so there isn't any purpose in 20% PWM? Apart that frequency should
> travel towards min when test context is in the idle PWM half? So how
> come that does not affect the measured values?
Idle periods are too small. The pulse width is 0.5ms, rps updates at
around 10ms, idle detection is around 50ms (hw) to 100ms-1000ms
(kernel). Plus worker latencies!
>
> > + }
> > +
> > + do {
> > + struct {
> > + uint32_t engine;
> > + uint32_t min;
> > + uint32_t max;
> > + } req = {};
>
> 3x local struct definition.
>
> > +
> > + 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)
>
> fixed? By idempotent I tought it will be one which cannot be modified.
The idempotent part of the uABI is that what the user feeds in, the user
gets back. That's the design goal (the argument is that is the least
surprising interface for the user), just rather than duplicate the values
in the kernel to achieve that goal, some slack is given. In this case,
that rounding happens to be useful for predicting expected frequencies.
So not strictly idempotent, just testing that principle and showing the
limitations in the API.
-Chris
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [igt-dev] [PATCH i-g-t] igt: Add gem_ctx_freq to exercise requesting freq on a ctx
2018-04-10 20:53 ` Chris Wilson
@ 2018-04-11 10:42 ` Tvrtko Ursulin
2018-04-11 11:12 ` Chris Wilson
0 siblings, 1 reply; 7+ messages in thread
From: Tvrtko Ursulin @ 2018-04-11 10:42 UTC (permalink / raw)
To: Chris Wilson, Arkadiusz Hiler, igt-dev; +Cc: Praveen Paneri
On 10/04/2018 21:53, Chris Wilson wrote:
> Quoting Tvrtko Ursulin (2018-04-10 17:17:13)
>>
>> On 16/03/2018 11:06, Arkadiusz Hiler wrote:
>>> From: Chris Wilson <chris@chris-wilson.co.uk>
>>>
>>> Exercise some new API that allows applications to request that
>>> individual contexts are executed within a desired frequency range.
[snip]
>>> +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);
>>
>> I would expect queued contexts would have different limits so here you
>> can check that the limit from the currently executing one remains in
>> force? Like it is I am not sure what it's testing?
>
> The challenge here is to detect that setting a frequency is done on a
> lite-restore, i.e. that the updated context frequency is used for the
> next batch even though the context is currently live.
I don't follow. Here you have ctx0 with freq set to min/min, then you
queue ctx1..16 all with freq set to min/min, and assert min is measured.
I don't see lite-restore or still the point of the test. If ctx1..16
would have freq set to something else you could assert the currently
running ctx did not have it's limit overwritten.
>>> + 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));
>>
>> gem_sync wouldn't cut it? Something internal could fool us here as the
>> queued up batches proceed to start and complete?
>
> gem_sync -> waitboost. I was writing this to avoid triggering
> waitboosts, and also to cancel out unwanted boosts between loops.
Okay, so helper with a comment would be good.
>>
>>> + igt_spin_batch_free(fd, plug);
>>> +
>>> + /* Now work will execute */
>>> + measured = measure_frequency(pmu, SAMPLE_PERIOD);
>>> + igt_debugfs_dump(fd, "i915_engine_info");
>>
>> Need this?
>
> Need it? Have you tried debugging it without it? :)
Is it a debugging leftover is where I was trying to get at. :)
>
>>> + 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);
>>
>> Assert it matches sysfs values?
>
> It doesn't. The context itself starts at hw limits, as the sysfs limits
> may change over time; and the whole shebang of limits is applied at the
> end to decide who wins.
But at this point how can a pristine context have different limits and
system global?
>>> + 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);
>>
>> Read-back ctx freq and check is correct?
>
> Define correct. We spent many test cycles to verify these are accurate
> at the points we can decide they are. Duplicating that fuzzy logic here,
> I did not want to do as it is not the test we are focusing on.
>
> It just happens that those basic API tests are after the behaviour
> tests. (Boring tests came after the tests to check the code was
> behaving.)
I was assuming that setting sysfs limits to something other than what
the context has set, the reading back the context limits, would read
back the updated values if they are more restrictive.
Maybe it is not the case?
So ctx freq cannot be set outside the _current_ global limits, but
global limits can be changed afterwards which query on ctx will not
reflect? Probably so, because otherwise you would have to walk all
contexts on sysfs writes.
>
>>> +
>>> + measured = measure_frequency(pmu, SAMPLE_PERIOD);
>>> + igt_debugfs_dump(fd, "i915_rps_boost_info");
>>> +
>>> + set_sysfs_freq(sys_min, sys_max);
>>
>> Just so we do not exit with random min/max set?
>
> Yes, as much as possible try to ensure we only assert and end the
> subtest on known state.
There is an exit handler already but it doesn't harm I guess.
>>
>>> + 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);
>>> + }
>>
>> Hmm.. so engines run in parallel - but how come all can assert they got
>> exactly the limits they wanted? Surely someone must get a different
>> range depending on what's running on another engine?
>
> That's the setup with the overlapping frequency ranges, it ensures that
> we can satisfy all the requests simultaneously. If the ranges are
> disjoint, then at least one engine's request cannot be satisfied.
Okay, but if one engine wants 200-800, and another 600-1000, how can
both assert that what they measure is in the range of what they asked
for? What will actually be the resulting range here? 200-1000 or
600-800? Hm I guess if 600-800 the assert works.
>>> +
>>> + 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));
>>
>> This loop should be moved to a helper since it is repeated a few times.
>> (If it is required in this form.)
>>
>>> + igt_spin_batch_free(fd, spin);
>>> + usleep(400);
>>
>> 100us/400us is I think too short if you want to get close to target
>> ratio. Will see later whether you actualy depend on it.
>
> The target ratio here is misleading, busyness is summed over engines, so
> a 5x pulses have a weird effect. I've no idea what the load should look
> like, the challenge is just to ensure that we do not have a 100% saturated
> load and so allow the GPU to downclock. To allow downclocking we have
> to be less than 65% busy (worst case). Other than by watching and
> confirming we up/downclock i.e. the loading was good enough, I don't
> think the pulses are the best method.
Why it is important to allow downclocking?
>
>>> + } 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);
>>
>> Have bi-directional comms so you know when child has processed the
>> updated request?
>
> Not just updated request, but the kernel rps worker to do something.
> A bit hazy.
Ties with the previous question and needs some big fat comments at least.
And what I forgot to say in the first pass - some of these subtests need
comments to explain the general idea of what they are about to do.
Regards,
Tvrtko
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [igt-dev] [PATCH i-g-t] igt: Add gem_ctx_freq to exercise requesting freq on a ctx
2018-04-11 10:42 ` Tvrtko Ursulin
@ 2018-04-11 11:12 ` Chris Wilson
0 siblings, 0 replies; 7+ messages in thread
From: Chris Wilson @ 2018-04-11 11:12 UTC (permalink / raw)
To: Tvrtko Ursulin, Arkadiusz Hiler, igt-dev; +Cc: Praveen Paneri
Quoting Tvrtko Ursulin (2018-04-11 11:42:12)
>
> On 10/04/2018 21:53, Chris Wilson wrote:
> > Quoting Tvrtko Ursulin (2018-04-10 17:17:13)
> >>
> >> On 16/03/2018 11:06, Arkadiusz Hiler wrote:
> >>> From: Chris Wilson <chris@chris-wilson.co.uk>
> >>>
> >>> Exercise some new API that allows applications to request that
> >>> individual contexts are executed within a desired frequency range.
>
> [snip]
>
> >>> +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);
> >>
> >> I would expect queued contexts would have different limits so here you
> >> can check that the limit from the currently executing one remains in
> >> force? Like it is I am not sure what it's testing?
> >
> > The challenge here is to detect that setting a frequency is done on a
> > lite-restore, i.e. that the updated context frequency is used for the
> > next batch even though the context is currently live.
>
> I don't follow. Here you have ctx0 with freq set to min/min, then you
> queue ctx1..16 all with freq set to min/min, and assert min is measured.
> I don't see lite-restore or still the point of the test. If ctx1..16
> would have freq set to something else you could assert the currently
> running ctx did not have it's limit overwritten.
This is just prechecks, the test is below.
> >>> + 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));
> >>
> >> gem_sync wouldn't cut it? Something internal could fool us here as the
> >> queued up batches proceed to start and complete?
> >
> > gem_sync -> waitboost. I was writing this to avoid triggering
> > waitboosts, and also to cancel out unwanted boosts between loops.
>
> Okay, so helper with a comment would be good.
>
> >>
> >>> + igt_spin_batch_free(fd, plug);
> >>> +
> >>> + /* Now work will execute */
> >>> + measured = measure_frequency(pmu, SAMPLE_PERIOD);
> >>> + igt_debugfs_dump(fd, "i915_engine_info");
> >>
> >> Need this?
> >
> > Need it? Have you tried debugging it without it? :)
>
> Is it a debugging leftover is where I was trying to get at. :)
Leftover wrt to debugging failures? No! It is "I need this information
to debug, so if CI either trips up, make sure we have it".
> >>> +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);
> >>
> >> Assert it matches sysfs values?
> >
> > It doesn't. The context itself starts at hw limits, as the sysfs limits
> > may change over time; and the whole shebang of limits is applied at the
> > end to decide who wins.
>
> But at this point how can a pristine context have different limits and
> system global?
How do you the external state is pristine? More to the point, how does
the test know that the sysfs limits won't interfere...
There is no tie between context range and sysfs range. It just happens
that the limits on both are given by the HW.
> >>> + 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);
> >>
> >> Read-back ctx freq and check is correct?
> >
> > Define correct. We spent many test cycles to verify these are accurate
> > at the points we can decide they are. Duplicating that fuzzy logic here,
> > I did not want to do as it is not the test we are focusing on.
> >
> > It just happens that those basic API tests are after the behaviour
> > tests. (Boring tests came after the tests to check the code was
> > behaving.)
>
> I was assuming that setting sysfs limits to something other than what
> the context has set, the reading back the context limits, would read
> back the updated values if they are more restrictive.
>
> Maybe it is not the case?
No. That is tricky user ABI (no idempotence at all, rather the current
rounding error).
ctx sets A, but reads back X, Y, Z depending on the phase of the moon.
User sets second context derived from first, sets any of X, Y, Z and
fails to get A.
> So ctx freq cannot be set outside the _current_ global limits, but
> global limits can be changed afterwards which query on ctx will not
> reflect? Probably so, because otherwise you would have to walk all
> contexts on sysfs writes.
ctx freq cannot be set outside of HW limits, but the RPS frequency
cannot be adjusted to outside of sysfs limits.
> >>> + measured = measure_frequency(pmu, SAMPLE_PERIOD);
> >>> + igt_debugfs_dump(fd, "i915_rps_boost_info");
> >>> +
> >>> + set_sysfs_freq(sys_min, sys_max);
> >>
> >> Just so we do not exit with random min/max set?
> >
> > Yes, as much as possible try to ensure we only assert and end the
> > subtest on known state.
>
> There is an exit handler already but it doesn't harm I guess.
Exit handlers are not run between subtests. (igt is not a good test
runner /rant)
> >>> + 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);
> >>> + }
> >>
> >> Hmm.. so engines run in parallel - but how come all can assert they got
> >> exactly the limits they wanted? Surely someone must get a different
> >> range depending on what's running on another engine?
> >
> > That's the setup with the overlapping frequency ranges, it ensures that
> > we can satisfy all the requests simultaneously. If the ranges are
> > disjoint, then at least one engine's request cannot be satisfied.
>
> Okay, but if one engine wants 200-800, and another 600-1000, how can
> both assert that what they measure is in the range of what they asked
> for? What will actually be the resulting range here? 200-1000 or
> 600-800? Hm I guess if 600-800 the assert works.
The resultant HW range will be [600-800] which intersects both engines,
and therefore both will be happy that the frequency is within their
target parameters.
If one engine requested [200, 200] and the other [1000, 1000], then one
will be disappointed. Hence we make sure that the ranges overlap for all
the requests such that the result is always intersects each.
Now what happens for the disjoint set of requests, I'm not going to tie
down as I think that is outside of the uABI of a single context. It is,
however, exercised by selftests.
> >>> + 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));
> >>
> >> This loop should be moved to a helper since it is repeated a few times.
> >> (If it is required in this form.)
> >>
> >>> + igt_spin_batch_free(fd, spin);
> >>> + usleep(400);
> >>
> >> 100us/400us is I think too short if you want to get close to target
> >> ratio. Will see later whether you actualy depend on it.
> >
> > The target ratio here is misleading, busyness is summed over engines, so
> > a 5x pulses have a weird effect. I've no idea what the load should look
> > like, the challenge is just to ensure that we do not have a 100% saturated
> > load and so allow the GPU to downclock. To allow downclocking we have
> > to be less than 65% busy (worst case). Other than by watching and
> > confirming we up/downclock i.e. the loading was good enough, I don't
> > think the pulses are the best method.
>
> Why it is important to allow downclocking?
It's hardly a smoke test if on every tick we pick max. The load should
ideally oscillate enough to hit both bounds.
> >>> + } 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);
> >>
> >> Have bi-directional comms so you know when child has processed the
> >> updated request?
> >
> > Not just updated request, but the kernel rps worker to do something.
> > A bit hazy.
>
> Ties with the previous question and needs some big fat comments at least.
But remember it is still a smoketest, an emulation of a live system to
check nothing explodes. Precision here is less paramount than a varied
workload trying to tickle the branches.
> And what I forgot to say in the first pass - some of these subtests need
> comments to explain the general idea of what they are about to do.
/* noise(); set freq; noise(); execute; read freq under load; compare */
;)
-Chris
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2018-04-11 11:12 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2018-03-16 11:06 [igt-dev] [PATCH i-g-t] igt: Add gem_ctx_freq to exercise requesting freq on a ctx Arkadiusz Hiler
2018-03-16 12:19 ` [igt-dev] ✓ Fi.CI.BAT: success for igt: Add gem_ctx_freq to exercise requesting freq on a ctx (rev7) Patchwork
2018-03-16 14:15 ` [igt-dev] ✓ Fi.CI.IGT: " Patchwork
2018-04-10 16:17 ` [igt-dev] [PATCH i-g-t] igt: Add gem_ctx_freq to exercise requesting freq on a ctx Tvrtko Ursulin
2018-04-10 20:53 ` Chris Wilson
2018-04-11 10:42 ` Tvrtko Ursulin
2018-04-11 11:12 ` Chris Wilson
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox