We [will] expose various per-engine scheduling controls. One of which, 'timeslice_duration_ms', defines the scheduling quantum. If a context exhausts its timeslice, it will be preempted in favour of running one of its compatriots. Signed-off-by: Chris Wilson <chris@xxxxxxxxxxxxxxxxxx> --- tests/Makefile.sources | 1 + tests/i915/sysfs_timeslice_duration.c | 310 ++++++++++++++++++++++++++ tests/meson.build | 1 + 3 files changed, 312 insertions(+) create mode 100644 tests/i915/sysfs_timeslice_duration.c diff --git a/tests/Makefile.sources b/tests/Makefile.sources index 4494d2c05..9bfe214d7 100644 --- a/tests/Makefile.sources +++ b/tests/Makefile.sources @@ -100,6 +100,7 @@ TESTS_progs = \ vgem_slow \ i915/sysfs_heartbeat_interval \ i915/sysfs_preemption_timeout \ + i915/sysfs_timeslice_duration \ $(NULL) TESTS_progs += gem_bad_reloc diff --git a/tests/i915/sysfs_timeslice_duration.c b/tests/i915/sysfs_timeslice_duration.c new file mode 100644 index 000000000..4c725923b --- /dev/null +++ b/tests/i915/sysfs_timeslice_duration.c @@ -0,0 +1,310 @@ +/* + * Copyright © 2019 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <inttypes.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "drmtest.h" /* gem_quiescent_gpu()! */ +#include "i915/gem_engine_topology.h" +#include "igt_dummyload.h" +#include "igt_sysfs.h" +#include "ioctl_wrappers.h" /* igt_require_gem()! */ +#include "sw_sync.h" + +#include "igt_debugfs.h" + +static bool __enable_hangcheck(int dir, bool state) +{ + return igt_sysfs_set(dir, "enable_hangcheck", state ? "1" : "0"); +} + +static bool enable_hangcheck(int i915, bool state) +{ + bool success; + int dir; + + dir = igt_sysfs_open_parameters(i915); + if (dir < 0) /* no parameters, must be default! */ + return false; + + success = __enable_hangcheck(dir, state); + close(dir); + + return success; +} + +static void set_timeslice(int engine, unsigned int value) +{ + unsigned int delay; + + igt_sysfs_printf(engine, "timeslice_duration_ms", "%u", value); + igt_sysfs_scanf(engine, "timeslice_duration_ms", "%u", &delay); + igt_assert_eq(delay, value); +} + +static void test_idempotent(int i915, int engine) +{ + const unsigned int delays[] = { 0, 1, 1234, 654321 }; + unsigned int saved; + + igt_assert(igt_sysfs_scanf(engine, "timeslice_duration_ms", "%u", &saved) == 1); + igt_debug("Initial timeslice_duration_ms:%u\n", saved); + + for (int i = 0; i < ARRAY_SIZE(delays); i++) + set_timeslice(engine, delays[i]); + + set_timeslice(engine, saved); +} + +static void test_invalid(int i915, int engine) +{ + unsigned int saved, delay; + + igt_assert(igt_sysfs_scanf(engine, "timeslice_duration_ms", "%u", &saved) == 1); + igt_debug("Initial timeslice_duration_ms:%u\n", saved); + + igt_sysfs_printf(engine, "timeslice_duration_ms", PRIu64, -1); + igt_sysfs_scanf(engine, "timeslice_duration_ms", "%u", &delay); + igt_assert_eq(delay, saved); + + igt_sysfs_printf(engine, "timeslice_duration_ms", "%d", -1); + igt_sysfs_scanf(engine, "timeslice_duration_ms", "%u", &delay); + igt_assert_eq(delay, saved); + + igt_sysfs_printf(engine, "timeslice_duration_ms", PRIu64, 123ull << 32); + igt_sysfs_scanf(engine, "timeslice_duration_ms", "%u", &delay); + igt_assert_eq(delay, saved); +} + +static void set_unbannable(int i915, uint32_t ctx) +{ + struct drm_i915_gem_context_param p = { + .ctx_id = ctx, + .param = I915_CONTEXT_PARAM_BANNABLE, + }; + + igt_assert_eq(__gem_context_set_param(i915, &p), 0); +} + +static uint32_t create_context(int i915, unsigned int class, unsigned int inst, int prio) +{ + uint32_t ctx; + + ctx = gem_context_create_for_engine(i915, class, inst); + set_unbannable(i915, ctx); + gem_context_set_priority(i915, ctx, prio); + + return ctx; +} + +static uint64_t __test_timeout(int i915, int engine, unsigned int timeout) +{ + unsigned int class, inst; + struct timespec ts = {}; + igt_spin_t *spin[2]; + uint64_t elapsed; + uint32_t ctx[2]; + + igt_assert(igt_sysfs_scanf(engine, "class", "%u", &class) == 1); + igt_assert(igt_sysfs_scanf(engine, "instance", "%u", &inst) == 1); + + igt_sysfs_printf(engine, "timeslice_duration_ms", "%u", timeout); + + ctx[0] = create_context(i915, class, inst, 0); + spin[0] = igt_spin_new(i915, ctx[0], + .flags = (IGT_SPIN_NO_PREEMPTION | + IGT_SPIN_POLL_RUN | + IGT_SPIN_FENCE_OUT)); + igt_spin_busywait_until_started(spin[0]); + + ctx[1] = create_context(i915, class, inst, 0); + igt_nsec_elapsed(&ts); + spin[1] = igt_spin_new(i915, ctx[1], .flags = IGT_SPIN_POLL_RUN); + igt_spin_busywait_until_started(spin[1]); + elapsed = igt_nsec_elapsed(&ts); + + igt_spin_free(i915, spin[1]); + + igt_assert_eq(sync_fence_wait(spin[0]->out_fence, 1), 0); + igt_assert_eq(sync_fence_status(spin[0]->out_fence), -EIO); + + igt_spin_free(i915, spin[0]); + + gem_context_destroy(i915, ctx[1]); + gem_context_destroy(i915, ctx[0]); + gem_quiescent_gpu(i915); + + return elapsed; +} + +static void test_timeout(int i915, int engine) +{ + int delays[] = { 1, 50, 100, 500 }; + unsigned int saved; + + igt_require(igt_sysfs_printf(engine, "preempt_timeout_ms", "%u", 1) == 1); + igt_assert(igt_sysfs_scanf(engine, "timeslice_duration_ms", "%u", &saved) == 1); + igt_debug("Initial timeslice_duration_ms:%u\n", saved); + + gem_quiescent_gpu(i915); + igt_require(enable_hangcheck(i915, false)); + + for (int i = 0; i < ARRAY_SIZE(delays); i++) { + uint64_t elapsed; + + elapsed = __test_timeout(i915, engine, delays[i]); + igt_info("timeslice_duration_ms:%d, elapsed=%.3fms\n", + delays[i], elapsed * 1e-6); + + /* + * We need to give a couple of jiffies slack for the scheduler timeouts + * and then a little more slack fr the overhead in submitting and + * measuring. 50ms should cover all of our sins and be useful + * tolerance. + */ + igt_assert_f(elapsed / 1000 / 1000 < delays[i] + 50, + "Forced preemption timeout exceeded request!\n"); + + } + + igt_assert(enable_hangcheck(i915, true)); + gem_quiescent_gpu(i915); + + set_timeslice(engine, saved); +} + +static void test_off(int i915, int engine) +{ + unsigned int class, inst; + unsigned int saved; + igt_spin_t *spin[2]; + uint32_t ctx[2]; + + igt_require(igt_sysfs_printf(engine, "preempt_timeout_ms", "%u", 1) == 1); + igt_assert(igt_sysfs_scanf(engine, "timeslice_duration_ms", "%u", &saved) == 1); + igt_debug("Initial timeslice_duration_ms:%u\n", saved); + + gem_quiescent_gpu(i915); + igt_require(enable_hangcheck(i915, false)); + + igt_assert(igt_sysfs_scanf(engine, "class", "%u", &class) == 1); + igt_assert(igt_sysfs_scanf(engine, "instance", "%u", &inst) == 1); + + igt_sysfs_printf(engine, "timeslice_duration_ms", "%u", 0); + + ctx[0] = create_context(i915, class, inst, 0); + spin[0] = igt_spin_new(i915, ctx[0], + .flags = (IGT_SPIN_NO_PREEMPTION | + IGT_SPIN_POLL_RUN | + IGT_SPIN_FENCE_OUT)); + igt_spin_busywait_until_started(spin[0]); + + ctx[1] = create_context(i915, class, inst, 0); + spin[1] = igt_spin_new(i915, ctx[1], .flags = IGT_SPIN_POLL_RUN); + + for (int i = 0; i < 150; i++) { + igt_assert_eq(sync_fence_status(spin[0]->out_fence), 0); + sleep(1); + } + + igt_sysfs_printf(engine, "timeslice_duration_ms", "%u", 1); + + igt_spin_busywait_until_started(spin[1]); + igt_spin_free(i915, spin[1]); + + igt_assert_eq(sync_fence_wait(spin[0]->out_fence, 1), 0); + igt_assert_eq(sync_fence_status(spin[0]->out_fence), -EIO); + + igt_spin_free(i915, spin[0]); + + gem_context_destroy(i915, ctx[1]); + gem_context_destroy(i915, ctx[0]); + + igt_assert(enable_hangcheck(i915, true)); + gem_quiescent_gpu(i915); + + set_timeslice(engine, saved); +} + +igt_main +{ + const struct intel_execution_engine2 *it; + int i915 = -1, engines = -1; + + igt_fixture { + int sys; + + i915 = drm_open_driver(DRIVER_INTEL); + igt_require_gem(i915); + + sys = igt_sysfs_open(i915); + igt_require(sys != -1); + + engines = openat(sys, "engine", O_RDONLY); + igt_require(engines != -1); + + close(sys); + } + + __for_each_static_engine(it) { + igt_subtest_group { + int engine = -1; + char *name = NULL; + + igt_fixture { + struct stat st; + + engine = openat(engines, it->name, O_RDONLY); + igt_require(fstatat(engine, + "timeslice_duration_ms", + &st, 0) == 0); + + name = igt_sysfs_get(engine, "name"); + } + if (!name) + continue; + + igt_subtest_f("%s-idempotent", name) + test_idempotent(i915, engine); + igt_subtest_f("%s-invalid", name) + test_invalid(i915, engine); + igt_subtest_f("%s-timeout", name) + test_timeout(i915, engine); + igt_subtest_f("%s-off", name) + test_off(i915, engine); + + free(name); + close(engine); + } + } + + igt_fixture { + close(engines); + close(i915); + } +} diff --git a/tests/meson.build b/tests/meson.build index 23f730c20..f603ab3e0 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -240,6 +240,7 @@ i915_progs = [ 'i915_suspend', 'sysfs_heartbeat_interval', 'sysfs_preemption_timeout', + 'sysfs_timeslice_duration', ] test_deps = [ igt_deps ] -- 2.24.0.rc0 _______________________________________________ Intel-gfx mailing list Intel-gfx@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/intel-gfx