diff options
-rw-r--r-- | tests/Makefile.am | 1 | ||||
-rw-r--r-- | tests/gem_eio.c | 235 | ||||
-rw-r--r-- | tests/meson.build | 8 |
3 files changed, 189 insertions, 55 deletions
diff --git a/tests/Makefile.am b/tests/Makefile.am index dbc7be72..f41ad509 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -130,6 +130,7 @@ gem_userptr_blits_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS) gem_userptr_blits_LDADD = $(LDADD) -lpthread perf_pmu_LDADD = $(LDADD) $(top_builddir)/lib/libigt_perf.la +gem_eio_LDADD = $(LDADD) -lrt gem_wait_LDADD = $(LDADD) -lrt kms_flip_LDADD = $(LDADD) -lrt -lpthread pm_rc6_residency_LDADD = $(LDADD) -lrt diff --git a/tests/gem_eio.c b/tests/gem_eio.c index 4bcc5937..b824d9d4 100644 --- a/tests/gem_eio.c +++ b/tests/gem_eio.c @@ -35,6 +35,8 @@ #include <inttypes.h> #include <errno.h> #include <sys/ioctl.h> +#include <signal.h> +#include <time.h> #include <drm.h> @@ -62,6 +64,10 @@ static bool i915_reset_control(bool enable) static void trigger_reset(int fd) { + struct timespec ts = { }; + + igt_nsec_elapsed(&ts); + igt_force_gpu_reset(fd); /* And just check the gpu is indeed running again */ @@ -69,22 +75,12 @@ static void trigger_reset(int fd) gem_test_engine(fd, ALL_ENGINES); gem_quiescent_gpu(fd); -} - -static void wedge_gpu(int fd) -{ - /* First idle the GPU then disable GPU resets before injecting a hang */ - gem_quiescent_gpu(fd); - - igt_require(i915_reset_control(false)); - - igt_debug("Wedging GPU by injecting hang\n"); - igt_post_hang_ring(fd, igt_hang_ring(fd, I915_EXEC_DEFAULT)); - igt_assert(i915_reset_control(true)); + /* We expect forced reset and health check to be quick. */ + igt_assert(igt_seconds_elapsed(&ts) < 2); } -static void wedgeme(int drm_fd) +static void manual_hang(int drm_fd) { int dir = igt_debugfs_dir(drm_fd); @@ -93,6 +89,16 @@ static void wedgeme(int drm_fd) close(dir); } +static void wedge_gpu(int fd) +{ + /* First idle the GPU then disable GPU resets before injecting a hang */ + gem_quiescent_gpu(fd); + + igt_require(i915_reset_control(false)); + manual_hang(fd); + igt_assert(i915_reset_control(true)); +} + static int __gem_throttle(int fd) { int err = 0; @@ -149,26 +155,124 @@ static int __gem_wait(int fd, uint32_t handle, int64_t timeout) return err; } -static void test_wait(int fd) +static igt_spin_t * __spin_poll(int fd, uint32_t ctx, unsigned long flags) { - igt_hang_t hang; + if (gem_can_store_dword(fd, flags)) + return __igt_spin_batch_new_poll(fd, ctx, flags); + else + return __igt_spin_batch_new(fd, ctx, flags, 0); +} + +static void __spin_wait(int fd, igt_spin_t *spin) +{ + if (spin->running) { + igt_spin_busywait_until_running(spin); + } else { + igt_debug("__spin_wait - usleep mode\n"); + usleep(500e3); /* Better than nothing! */ + } +} + +static igt_spin_t * spin_sync(int fd, uint32_t ctx, unsigned long flags) +{ + igt_spin_t *spin = __spin_poll(fd, ctx, flags); + + __spin_wait(fd, spin); + + return spin; +} + +struct hang_ctx { + int debugfs; + struct timespec ts; + timer_t timer; +}; + +static void hang_handler(union sigval arg) +{ + struct hang_ctx *ctx = arg.sival_ptr; + + igt_debug("hang delay = %.2fus\n", igt_nsec_elapsed(&ctx->ts) / 1000.0); + + igt_assert(igt_sysfs_set(ctx->debugfs, "i915_wedged", "-1")); + + igt_assert_eq(timer_delete(ctx->timer), 0); + close(ctx->debugfs); + free(ctx); +} + +static void hang_after(int fd, unsigned int us) +{ + struct sigevent sev = { + .sigev_notify = SIGEV_THREAD, + .sigev_notify_function = hang_handler + }; + struct itimerspec its = { + .it_value.tv_sec = us / USEC_PER_SEC, + .it_value.tv_nsec = us % USEC_PER_SEC * 1000, + }; + struct hang_ctx *ctx; + + ctx = calloc(1, sizeof(*ctx)); + igt_assert(ctx); + + ctx->debugfs = igt_debugfs_dir(fd); + igt_assert_fd(ctx->debugfs); + + sev.sigev_value.sival_ptr = ctx; + + igt_assert_eq(timer_create(CLOCK_MONOTONIC, &sev, &ctx->timer), 0); + + igt_nsec_elapsed(&ctx->ts); + + igt_assert_eq(timer_settime(ctx->timer, 0, &its, NULL), 0); +} + +static int __check_wait(int fd, uint32_t bo, unsigned int wait) +{ + unsigned long wait_timeout = 250e6; /* Some margin for actual reset. */ + int ret; + + if (wait) { + /* + * Double the wait plus some fixed margin to ensure gem_wait + * can never time out before the async hang runs. + */ + wait_timeout += wait * 2000 + 250e6; + hang_after(fd, wait); + } else { + manual_hang(fd); + } + + ret = __gem_wait(fd, bo, wait_timeout); + + return ret; +} + +#define TEST_WEDGE (1) + +static void test_wait(int fd, unsigned int flags, unsigned int wait) +{ + igt_spin_t *hang; igt_require_gem(fd); - /* If the request we wait on completes due to a hang (even for + /* + * If the request we wait on completes due to a hang (even for * that request), the user expects the return value to 0 (success). */ - hang = igt_hang_ring(fd, I915_EXEC_DEFAULT); - igt_assert_eq(__gem_wait(fd, hang.handle, -1), 0); - igt_post_hang_ring(fd, hang); - /* If the GPU is wedged during the wait, again we expect the return - * value to be 0 (success). - */ - igt_require(i915_reset_control(false)); - hang = igt_hang_ring(fd, I915_EXEC_DEFAULT); - igt_assert_eq(__gem_wait(fd, hang.handle, -1), 0); - igt_post_hang_ring(fd, hang); + if (flags & TEST_WEDGE) + igt_require(i915_reset_control(false)); + else + igt_require(i915_reset_control(true)); + + hang = spin_sync(fd, 0, I915_EXEC_DEFAULT); + + igt_assert_eq(__check_wait(fd, hang->handle, wait), 0); + + igt_spin_batch_free(fd, hang); + igt_require(i915_reset_control(true)); trigger_reset(fd); @@ -181,7 +285,7 @@ static void test_suspend(int fd, int state) /* Check we can suspend when the driver is already wedged */ igt_require(i915_reset_control(false)); - wedgeme(fd); + manual_hang(fd); igt_system_suspend_autoresume(state, SUSPEND_TEST_DEVICES); @@ -189,7 +293,7 @@ static void test_suspend(int fd, int state) trigger_reset(fd); } -static void test_inflight(int fd) +static void test_inflight(int fd, unsigned int wait) { const uint32_t bbe = MI_BATCH_BUFFER_END; struct drm_i915_gem_exec_object2 obj[2]; @@ -209,11 +313,10 @@ static void test_inflight(int fd) int fence[64]; /* conservative estimate of ring size */ gem_quiescent_gpu(fd); - igt_debug("Starting %s on engine '%s'\n", __func__, e__->name); igt_require(i915_reset_control(false)); - hang = __igt_spin_batch_new(fd, 0, engine, 0); + hang = spin_sync(fd, 0, engine); obj[0].handle = hang->handle; memset(&execbuf, 0, sizeof(execbuf)); @@ -227,7 +330,8 @@ static void test_inflight(int fd) igt_assert(fence[n] != -1); } - igt_assert_eq(__gem_wait(fd, obj[1].handle, -1), 0); + igt_assert_eq(__check_wait(fd, obj[1].handle, wait), 0); + for (unsigned int n = 0; n < ARRAY_SIZE(fence); n++) { igt_assert_eq(sync_fence_status(fence[n]), -EIO); close(fence[n]); @@ -256,7 +360,7 @@ static void test_inflight_suspend(int fd) obj[1].handle = gem_create(fd, 4096); gem_write(fd, obj[1].handle, 0, &bbe, sizeof(bbe)); - hang = __igt_spin_batch_new(fd, 0, 0, 0); + hang = spin_sync(fd, 0, 0); obj[0].handle = hang->handle; memset(&execbuf, 0, sizeof(execbuf)); @@ -273,7 +377,8 @@ static void test_inflight_suspend(int fd) igt_set_autoresume_delay(30); igt_system_suspend_autoresume(SUSPEND_STATE_MEM, SUSPEND_TEST_NONE); - igt_assert_eq(__gem_wait(fd, obj[1].handle, -1), 0); + igt_assert_eq(__check_wait(fd, obj[1].handle, 10), 0); + for (unsigned int n = 0; n < ARRAY_SIZE(fence); n++) { igt_assert_eq(sync_fence_status(fence[n]), -EIO); close(fence[n]); @@ -301,7 +406,7 @@ static uint32_t context_create_safe(int i915) return param.ctx_id; } -static void test_inflight_contexts(int fd) +static void test_inflight_contexts(int fd, unsigned int wait) { struct drm_i915_gem_exec_object2 obj[2]; const uint32_t bbe = MI_BATCH_BUFFER_END; @@ -330,7 +435,7 @@ static void test_inflight_contexts(int fd) igt_debug("Starting %s on engine '%s'\n", __func__, e__->name); igt_require(i915_reset_control(false)); - hang = __igt_spin_batch_new(fd, 0, engine, 0); + hang = spin_sync(fd, 0, engine); obj[0].handle = hang->handle; memset(&execbuf, 0, sizeof(execbuf)); @@ -345,7 +450,8 @@ static void test_inflight_contexts(int fd) igt_assert(fence[n] != -1); } - igt_assert_eq(__gem_wait(fd, obj[1].handle, -1), 0); + igt_assert_eq(__check_wait(fd, obj[1].handle, wait), 0); + for (unsigned int n = 0; n < ARRAY_SIZE(fence); n++) { igt_assert_eq(sync_fence_status(fence[n]), -EIO); close(fence[n]); @@ -375,7 +481,7 @@ static void test_inflight_external(int fd) fence = igt_cork_plug(&cork, fd); igt_require(i915_reset_control(false)); - hang = __igt_spin_batch_new(fd, 0, 0, 0); + hang = __spin_poll(fd, 0, 0); memset(&obj, 0, sizeof(obj)); obj.handle = gem_create(fd, 4096); @@ -393,6 +499,9 @@ static void test_inflight_external(int fd) fence = execbuf.rsvd2 >> 32; igt_assert(fence != -1); + __spin_wait(fd, hang); + manual_hang(fd); + gem_sync(fd, hang->handle); /* wedged, with an unready batch */ igt_assert(!gem_bo_busy(fd, hang->handle)); igt_assert(gem_bo_busy(fd, obj.handle)); @@ -407,7 +516,7 @@ static void test_inflight_external(int fd) trigger_reset(fd); } -static void test_inflight_internal(int fd) +static void test_inflight_internal(int fd, unsigned int wait) { struct drm_i915_gem_execbuffer2 execbuf; struct drm_i915_gem_exec_object2 obj[2]; @@ -420,7 +529,7 @@ static void test_inflight_internal(int fd) igt_require(gem_has_exec_fence(fd)); igt_require(i915_reset_control(false)); - hang = __igt_spin_batch_new(fd, 0, 0, 0); + hang = spin_sync(fd, 0, 0); memset(obj, 0, sizeof(obj)); obj[0].handle = hang->handle; @@ -441,7 +550,8 @@ static void test_inflight_internal(int fd) nfence++; } - igt_assert_eq(__gem_wait(fd, obj[1].handle, -1), 0); + igt_assert_eq(__check_wait(fd, obj[1].handle, wait), 0); + while (nfence--) { igt_assert_eq(sync_fence_status(fences[nfence]), -EIO); close(fences[nfence]); @@ -484,29 +594,46 @@ igt_main igt_subtest("execbuf") test_execbuf(fd); - igt_subtest("wait") - test_wait(fd); - igt_subtest("suspend") test_suspend(fd, SUSPEND_STATE_MEM); igt_subtest("hibernate") test_suspend(fd, SUSPEND_STATE_DISK); - igt_subtest("in-flight") - test_inflight(fd); - - igt_subtest("in-flight-contexts") - test_inflight_contexts(fd); - igt_subtest("in-flight-external") test_inflight_external(fd); - igt_subtest("in-flight-internal") { - igt_skip_on(gem_has_semaphores(fd)); - test_inflight_internal(fd); - } - igt_subtest("in-flight-suspend") test_inflight_suspend(fd); + + igt_subtest_group { + const struct { + unsigned int wait; + const char *name; + } waits[] = { + { .wait = 0, .name = "immediate" }, + { .wait = 1, .name = "1us" }, + { .wait = 10000, .name = "10ms" }, + }; + unsigned int i; + + for (i = 0; i < sizeof(waits) / sizeof(waits[0]); i++) { + igt_subtest_f("wait-%s", waits[i].name) + test_wait(fd, 0, waits[i].wait); + + igt_subtest_f("wait-wedge-%s", waits[i].name) + test_wait(fd, TEST_WEDGE, waits[i].wait); + + igt_subtest_f("in-flight-%s", waits[i].name) + test_inflight(fd, waits[i].wait); + + igt_subtest_f("in-flight-contexts-%s", waits[i].name) + test_inflight_contexts(fd, waits[i].wait); + + igt_subtest_f("in-flight-internal-%s", waits[i].name) { + igt_skip_on(gem_has_semaphores(fd)); + test_inflight_internal(fd, waits[i].wait); + } + } + } } diff --git a/tests/meson.build b/tests/meson.build index 122aefab..4720dfe2 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -40,7 +40,6 @@ test_progs = [ 'gem_ctx_switch', 'gem_ctx_thrash', 'gem_double_irq_loop', - 'gem_eio', 'gem_evict_alignment', 'gem_evict_everything', 'gem_exec_alignment', @@ -289,6 +288,13 @@ foreach prog : test_progs install : true) endforeach +test_executables += executable('gem_eio', 'gem_eio.c', + dependencies : test_deps + [ realtime ], + install_dir : libexecdir, + install_rpath : rpathdir, + install : true) +test_progs += 'gem_eio' + test_executables += executable('perf_pmu', 'perf_pmu.c', dependencies : test_deps + [ lib_igt_perf ], install_dir : libexecdir, |