[igt-dev] [PATCH i-g-t 2/3] tests/gem_eio: Speed up test execution

Chris Wilson chris at chris-wilson.co.uk
Thu Mar 22 17:44:38 UTC 2018


Quoting Tvrtko Ursulin (2018-03-22 17:24:16)
> From: Tvrtko Ursulin <tvrtko.ursulin at intel.com>
> 
> If we stop relying on regular GPU hangs to be detected, but trigger them
> manually as soon as we know our batch of interest is actually executing
> on the GPU, we can dramatically speed up various subtests.
> 
> This is enabled by the pollable spin batch added in the previous patch.
> 
> v2:
>  * Test gem_wait after reset/wedge and with reset/wedge after a few
>    predefined intervals since gem_wait invocation. (Chris Wilson)
> 
> Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin at intel.com>
> Suggested-by: Chris Wilson <chris at chris-wilson.co.uk>
> Cc: Antonio Argenziano <antonio.argenziano at intel.com>
> ---
>  lib.tar         | Bin 0 -> 102400 bytes
>  tests/gem_eio.c | 214 ++++++++++++++++++++++++++++++++++++++++++--------------
>  2 files changed, 160 insertions(+), 54 deletions(-)
>  create mode 100644 lib.tar
> 
> diff --git a/tests/gem_eio.c b/tests/gem_eio.c
> index 4bcc5937db39..2f9e954085ec 100644
> --- a/tests/gem_eio.c
> +++ b/tests/gem_eio.c
> @@ -35,6 +35,7 @@
>  #include <inttypes.h>
>  #include <errno.h>
>  #include <sys/ioctl.h>
> +#include <signal.h>
>  
>  #include <drm.h>
>  
> @@ -71,26 +72,23 @@ static void trigger_reset(int fd)
>         gem_quiescent_gpu(fd);
>  }
>  
> -static void wedge_gpu(int fd)
> +static void manual_hang(int drm_fd)
>  {
> -       /* First idle the GPU then disable GPU resets before injecting a hang */
> -       gem_quiescent_gpu(fd);
> -
> -       igt_require(i915_reset_control(false));
> +       int dir = igt_debugfs_dir(drm_fd);
>  
> -       igt_debug("Wedging GPU by injecting hang\n");
> -       igt_post_hang_ring(fd, igt_hang_ring(fd, I915_EXEC_DEFAULT));
> +       igt_sysfs_set(dir, "i915_wedged", "-1");
>  
> -       igt_assert(i915_reset_control(true));
> +       close(dir);
>  }
>  
> -static void wedgeme(int drm_fd)
> +static void wedge_gpu(int fd)
>  {
> -       int dir = igt_debugfs_dir(drm_fd);
> -
> -       igt_sysfs_set(dir, "i915_wedged", "-1");
> +       /* First idle the GPU then disable GPU resets before injecting a hang */
> +       gem_quiescent_gpu(fd);
>  
> -       close(dir);
> +       igt_require(i915_reset_control(false));
> +       manual_hang(fd);
> +       igt_assert(i915_reset_control(true));
>  }
>  
>  static int __gem_throttle(int fd)
> @@ -149,26 +147,111 @@ 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)
> +{
> +       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;
> +}
> +
> +static int debugfs_dir = -1;
> +
> +static void hang_handler(int sig)
> +{
> +       igt_sysfs_set(debugfs_dir, "i915_wedged", "-1");
> +}
> +
> +static void hang_after(int fd, unsigned int us)
> +{
> +        struct sigaction sa = { .sa_handler = hang_handler };
> +       struct itimerval itv = { };
> +
> +       debugfs_dir = igt_debugfs_dir(fd);
> +       igt_assert_fd(debugfs_dir);
> +
> +       igt_assert_eq(sigaction(SIGALRM, &sa, NULL), 0);
> +
> +       itv.it_value.tv_sec = us / 1000000;

USEC_PER_SEC.

> +       itv.it_value.tv_usec = us % 1000000;
> +       setitimer(ITIMER_REAL, &itv, NULL);

Ok, that gives a single shot signal.

I would have used
struct sigevent sev = {
	.sigev_notify = SIGEV_THREAD,
	.sigev_value.sigval_int = debugfs_dir
	.sigev_notify_function = hang_handler
};
timer_create(CLOCK_MONOTONIC, &sec, &timer);
timer_settime(timer, 0, &its, NULL);

Then 

static void hang_handler(union sigval arg)
{
	igt_sysfs_set(arg.sival_int, "i915_wedged", 1);
}

No signals, nor globals required :)

The problem with using a signal is that it interrupts the gem_wait()
and so we don't actually check that it is being woken by the hang
because it is already awake. Gah.

> +static void cleanup_hang(void)
> +{
> +       struct itimerval itv = { };
> +
> +       igt_assert_eq(setitimer(ITIMER_REAL, &itv, NULL), 0);

You also need a sleep here as it does not flush inflight signals.
(Also timer_destroy :)

> +       igt_assert_fd(debugfs_dir);
> +       close(debugfs_dir);
> +       debugfs_dir = -1;
> +}
> +
> +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) {
> +               wait_timeout += wait * 2000; /* x2 for safety. */
> +               wait_timeout += 250e6; /* Margin for signal delay. */;
> +               hang_after(fd, wait);
> +       } else {
> +               manual_hang(fd);
> +       }
> +
> +       ret = __gem_wait(fd, bo, wait_timeout);

Ok, I understand where the concerned about how long it took to recover
from reset came from :)

> +
> +       if (wait)
> +               cleanup_hang();
> +
> +       return ret;
> +}
> +
> +#define TEST_WEDGE (1)
> +
> +static void test_wait(int fd, unsigned int flags, unsigned int wait)
>  {
> -       igt_hang_t hang;
> +       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 +264,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 +272,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 +292,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 +309,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 +339,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 +356,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 +385,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 +414,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 +429,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 +460,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 +478,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 +495,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 +508,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 +529,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 +573,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 = 10, .name = "10us" },

i915_request_spin is set to 2us currently :| I guess that's a really hard
window to hit reliably. Maybe we should spin for 200ms just to make
testing easier!

> +                       { .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);

Ok.

> +
> +                       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);

And ok.
-Chris


More information about the igt-dev mailing list