Rather than guestimating a workload that should take a certain amount of time, use a sigitimer to terminate a batch (and so complete the wait) after an exact amount of time. And in the process expand testing to cover multiple rings and hangcheck. Signed-off-by: Chris Wilson <chris@xxxxxxxxxxxxxxxxxx> --- lib/igt_core.h | 27 ++++ tests/gem_wait.c | 443 +++++++++++++++++++++++++++++++------------------------ 2 files changed, 277 insertions(+), 193 deletions(-) diff --git a/lib/igt_core.h b/lib/igt_core.h index 433b88c..03be757 100644 --- a/lib/igt_core.h +++ b/lib/igt_core.h @@ -403,6 +403,24 @@ static inline void igt_ignore_warn(bool value) } while (0) /** + * igt_assert_cmps64: + * @n1: first value + * @cmp: compare operator + * @ncmp: negated version of @cmp + * @n2: second value + * + * Like igt_assert_cmpuint(), but for larger signed ints. + */ +#define igt_assert_cmps64(n1, cmp, ncmp, n2) \ + do { \ + int64_t __n1 = (n1), __n2 = (n2); \ + if (__n1 cmp __n2) ; else \ + __igt_fail_assert(IGT_LOG_DOMAIN, __FILE__, __LINE__, __func__, \ + #n1 " " #cmp " " #n2, \ + "error: %lld " #ncmp " %lld\n", (long long)__n1, (long long)__n2); \ + } while (0) + +/** * igt_assert_cmpu64: * @n1: first value * @cmp: compare operator @@ -461,6 +479,15 @@ static inline void igt_ignore_warn(bool value) #define igt_assert_eq_u32(n1, n2) igt_assert_cmpuint(n1, ==, !=, n2) /** + * igt_assert_eq_s64: + * @n1: first integer + * @n2: second integer + * + * Like igt_assert_eq_u32(), but for int64_t. + */ +#define igt_assert_eq_s64(n1, n2) igt_assert_cmps64(n1, ==, !=, n2) + +/** * igt_assert_eq_u64: * @n1: first integer * @n2: second integer diff --git a/tests/gem_wait.c b/tests/gem_wait.c index 461efdb..0ecb92f 100644 --- a/tests/gem_wait.c +++ b/tests/gem_wait.c @@ -26,233 +26,290 @@ */ #include "igt.h" -#include <stdio.h> + +#include <signal.h> #include <time.h> -#include <stdlib.h> -#include <sys/ioctl.h> -#include <stdio.h> -#include <string.h> -#include <fcntl.h> -#include <inttypes.h> -#include <errno.h> -#include <sys/stat.h> -#include <sys/time.h> - -#include <drm.h> - -#include "intel_bufmgr.h" - -#define MSEC_PER_SEC 1000L -#define USEC_PER_MSEC 1000L -#define NSEC_PER_USEC 1000L -#define NSEC_PER_MSEC 1000000L -#define USEC_PER_SEC 1000000L -#define NSEC_PER_SEC 1000000000L - -#define ENOUGH_WORK_IN_SECONDS 2 -#define BUF_SIZE (8<<20) -#define BUF_PAGES ((8<<20)>>12) -drm_intel_bo *dst, *dst2; - -/* returns time diff in milliseconds */ -static int64_t -do_time_diff(struct timespec *end, struct timespec *start) -{ - int64_t ret; - ret = (MSEC_PER_SEC * difftime(end->tv_sec, start->tv_sec)) + - ((end->tv_nsec/NSEC_PER_MSEC) - (start->tv_nsec/NSEC_PER_MSEC)); - return ret; -} +#include <sys/syscall.h> -static void blt_color_fill(struct intel_batchbuffer *batch, - drm_intel_bo *buf, - const unsigned int pages) -{ - const unsigned short height = pages/4; - const unsigned short width = 4096; - - COLOR_BLIT_COPY_BATCH_START(COLOR_BLT_WRITE_ALPHA | - XY_COLOR_BLT_WRITE_RGB); - OUT_BATCH((3 << 24) | /* 32 Bit Color */ - (0xF0 << 16) | /* Raster OP copy background register */ - 0); /* Dest pitch is 0 */ - OUT_BATCH(0); - OUT_BATCH(width << 16 | - height); - OUT_RELOC_FENCED(buf, I915_GEM_DOMAIN_RENDER, I915_GEM_DOMAIN_RENDER, 0); - OUT_BATCH(rand()); /* random pattern */ - ADVANCE_BATCH(); -} +#define gettid() syscall(__NR_gettid) +#define sigev_notify_thread_id _sigev_un._tid -static void render_timeout(int fd) +#define LOCAL_I915_EXEC_BSD_SHIFT (13) +#define LOCAL_I915_EXEC_BSD_MASK (3 << LOCAL_I915_EXEC_BSD_SHIFT) + +#define ENGINE_MASK (I915_EXEC_RING_MASK | LOCAL_I915_EXEC_BSD_MASK) + +static int __gem_wait(int fd, struct drm_i915_gem_wait *w) { - drm_intel_bufmgr *bufmgr; - struct intel_batchbuffer *batch; - int64_t timeout = ENOUGH_WORK_IN_SECONDS * NSEC_PER_SEC; - int64_t negative_timeout = -1; - int ret; - const bool do_signals = true; /* signals will seem to make the operation - * use less process CPU time */ - bool done = false; - int i, iter = 1; + int err; - igt_skip_on_simulation(); + err = 0; + if (igt_ioctl(fd, DRM_IOCTL_I915_GEM_WAIT, w)) + err = -errno; - bufmgr = drm_intel_bufmgr_gem_init(fd, 4096); - drm_intel_bufmgr_gem_enable_reuse(bufmgr); - batch = intel_batchbuffer_alloc(bufmgr, intel_get_drm_devid(fd)); - - dst = drm_intel_bo_alloc(bufmgr, "dst", BUF_SIZE, 4096); - dst2 = drm_intel_bo_alloc(bufmgr, "dst2", BUF_SIZE, 4096); - - igt_skip_on_f(gem_wait(fd, dst->handle, &timeout) == -EINVAL, - "kernel doesn't support wait_timeout, skipping test\n"); - timeout = ENOUGH_WORK_IN_SECONDS * NSEC_PER_SEC; - - /* Figure out a rough number of fills required to consume 1 second of - * GPU work. - */ - do { - struct timespec start, end; - long diff; - -#ifndef CLOCK_MONOTONIC_RAW -#define CLOCK_MONOTONIC_RAW CLOCK_MONOTONIC -#endif - - igt_assert(clock_gettime(CLOCK_MONOTONIC_RAW, &start) == 0); - for (i = 0; i < iter; i++) - blt_color_fill(batch, dst, BUF_PAGES); - intel_batchbuffer_flush(batch); - drm_intel_bo_wait_rendering(dst); - igt_assert(clock_gettime(CLOCK_MONOTONIC_RAW, &end) == 0); - - diff = do_time_diff(&end, &start); - igt_assert(diff >= 0); - - if ((diff / MSEC_PER_SEC) > ENOUGH_WORK_IN_SECONDS) - done = true; - else - iter <<= 1; - } while (!done && iter < 1000000); - - igt_assert_lt(iter, 1000000); - - igt_debug("%d iters is enough work\n", iter); - gem_quiescent_gpu(fd); - if (do_signals) - igt_fork_signal_helper(); - - /* We should be able to do half as much work in the same amount of time, - * but because we might schedule almost twice as much as required, we - * might accidentally time out. Hence add some fudge. */ - for (i = 0; i < iter/3; i++) - blt_color_fill(batch, dst2, BUF_PAGES); - - intel_batchbuffer_flush(batch); - igt_assert(gem_bo_busy(fd, dst2->handle) == true); - - igt_assert_eq(gem_wait(fd, dst2->handle, &timeout), 0); - igt_assert(gem_bo_busy(fd, dst2->handle) == false); - igt_assert_neq(timeout, 0); - if (timeout == (ENOUGH_WORK_IN_SECONDS * NSEC_PER_SEC)) - igt_info("Buffer was already done!\n"); - else - igt_info("Finished with %fs remaining\n", timeout*1e-9); - - /* check that polling with timeout=0 works. */ - timeout = 0; - igt_assert_eq(gem_wait(fd, dst2->handle, &timeout), 0); - igt_assert_eq(timeout, 0); - - /* Now check that we correctly time out, twice the auto-tune load should - * be good enough. */ - timeout = ENOUGH_WORK_IN_SECONDS * NSEC_PER_SEC; - for (i = 0; i < iter*2; i++) - blt_color_fill(batch, dst2, BUF_PAGES); - - intel_batchbuffer_flush(batch); - - ret = gem_wait(fd, dst2->handle, &timeout); - igt_assert_eq(ret, -ETIME); - igt_assert_eq(timeout, 0); - igt_assert(gem_bo_busy(fd, dst2->handle) == true); - - /* check that polling with timeout=0 works. */ - timeout = 0; - igt_assert_eq(gem_wait(fd, dst2->handle, &timeout), -ETIME); - igt_assert_eq(timeout, 0); - - - /* Now check that we can pass negative (infinite) timeouts. */ - negative_timeout = -1; - for (i = 0; i < iter; i++) - blt_color_fill(batch, dst2, BUF_PAGES); - - intel_batchbuffer_flush(batch); - - igt_assert_eq(gem_wait(fd, dst2->handle, &negative_timeout), 0); - igt_assert_eq(negative_timeout, -1); /* infinity always remains */ - igt_assert(gem_bo_busy(fd, dst2->handle) == false); - - if (do_signals) - igt_stop_signal_helper(); - drm_intel_bo_unreference(dst2); - drm_intel_bo_unreference(dst); - intel_batchbuffer_free(batch); - drm_intel_bufmgr_destroy(bufmgr); + return err; } static void invalid_flags(int fd) { struct drm_i915_gem_wait wait; - int ret; - uint32_t handle; - handle = gem_create(fd, 4096); - - wait.bo_handle = handle; + memset(&wait, 0, sizeof(wait)); + wait.bo_handle = gem_create(fd, 4096); wait.timeout_ns = 1; /* NOTE: This test intentionally tests for just the next available flag. * Don't "fix" this testcase without the ABI testcases for new flags * first. */ wait.flags = 1; - ret = drmIoctl(fd, DRM_IOCTL_I915_GEM_WAIT, &wait); - igt_assert(ret != 0 && errno == EINVAL); + igt_assert_eq(__gem_wait(fd, &wait), -EINVAL); - gem_close(fd, handle); + gem_close(fd, wait.bo_handle); } static void invalid_buf(int fd) { struct drm_i915_gem_wait wait; - int ret; - wait.bo_handle = 0; - wait.timeout_ns = 1; - wait.flags = 0; - ret = drmIoctl(fd, DRM_IOCTL_I915_GEM_WAIT, &wait); + memset(&wait, 0, sizeof(wait)); + igt_assert_eq(__gem_wait(fd, &wait), -ENOENT); +} + +static uint32_t *batch; - igt_assert(ret != 0 && errno == ENOENT); +static void sigiter(int sig, siginfo_t *info, void *arg) +{ + *batch = MI_BATCH_BUFFER_END; + __sync_synchronize(); } -int drm_fd; +#define MSEC_PER_SEC (1000) +#define USEC_PER_SEC (1000 * MSEC_PER_SEC) +#define NSEC_PER_SEC (1000 * USEC_PER_SEC) + +#define BUSY 1 +#define HANG 2 +static void basic(int fd, unsigned engine, unsigned flags) +{ + const int gen = intel_gen(intel_get_drm_devid(fd)); + struct drm_i915_gem_exec_object2 obj; + struct drm_i915_gem_relocation_entry reloc; + struct drm_i915_gem_execbuffer2 execbuf; + struct drm_i915_gem_wait wait; + unsigned engines[16]; + unsigned nengine; + int i, timeout; + + nengine = 0; + if (engine == -1) { + for_each_engine(fd, engine) + if (engine) engines[nengine++] = engine; + } else { + igt_require(gem_has_ring(fd, engine)); + engines[nengine++] = engine; + } + igt_require(nengine); + + memset(&execbuf, 0, sizeof(execbuf)); + execbuf.buffers_ptr = (uintptr_t)&obj; + execbuf.buffer_count = 1; + + memset(&obj, 0, sizeof(obj)); + obj.handle = gem_create(fd, 4096); + + obj.relocs_ptr = (uintptr_t)&reloc; + obj.relocation_count = 1; + memset(&reloc, 0, sizeof(reloc)); + + batch = gem_mmap__gtt(fd, obj.handle, 4096, PROT_WRITE); + gem_set_domain(fd, obj.handle, + I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT); + + reloc.target_handle = obj.handle; /* recurse */ + reloc.presumed_offset = 0; + reloc.offset = sizeof(uint32_t); + reloc.delta = 0; + reloc.read_domains = I915_GEM_DOMAIN_COMMAND; + reloc.write_domain = 0; + + i = 0; + batch[i] = MI_BATCH_BUFFER_START; + if (gen >= 8) { + batch[i] |= 1 << 8 | 1; + batch[++i] = 0; + batch[++i] = 0; + } else if (gen >= 6) { + batch[i] |= 1 << 8; + batch[++i] = 0; + } else { + batch[i] |= 2 << 6; + batch[++i] = 0; + if (gen < 4) { + batch[i] |= 1; + reloc.delta = 1; + } + } + + for (i = 0; i < nengine; i++) { + execbuf.flags &= ~ENGINE_MASK; + execbuf.flags |= engines[i]; + gem_execbuf(fd, &execbuf); + } + + memset(&wait, 0, sizeof(wait)); + wait.bo_handle = obj.handle; + igt_assert_eq(__gem_wait(fd, &wait), -ETIME); + + if (flags & BUSY) { + struct timespec tv; + + timeout = 120; + if ((flags & HANG) == 0) { + *batch = MI_BATCH_BUFFER_END; + __sync_synchronize(); + timeout = 1; + } + munmap(batch, 4096); + + memset(&tv, 0, sizeof(tv)); + while (__gem_wait(fd, &wait) == -ETIME) + igt_assert(igt_seconds_elapsed(&tv) < timeout); + } else { + timer_t timer; + + if ((flags & HANG) == 0) { + struct sigevent sev; + struct sigaction act; + struct itimerspec its; + + memset(&sev, 0, sizeof(sev)); + sev.sigev_notify = SIGEV_SIGNAL | SIGEV_THREAD_ID; + sev.sigev_notify_thread_id = gettid(); + sev.sigev_signo = SIGRTMIN + 1; + igt_assert(timer_create(CLOCK_MONOTONIC, &sev, &timer) == 0); + + memset(&act, 0, sizeof(act)); + act.sa_sigaction = sigiter; + act.sa_flags = SA_SIGINFO; + igt_assert(sigaction(SIGRTMIN + 1, &act, NULL) == 0); + + memset(&its, 0, sizeof(its)); + its.it_value.tv_nsec = 0; + its.it_value.tv_sec = 1; + igt_assert(timer_settime(timer, 0, &its, NULL) == 0); + } + + wait.timeout_ns = NSEC_PER_SEC / 2; /* 0.5s */ + igt_assert_eq(__gem_wait(fd, &wait), -ETIME); + igt_assert_eq_s64(wait.timeout_ns, 0); + + if ((flags & HANG) == 0) { + wait.timeout_ns = NSEC_PER_SEC; /* 1.0s */ + igt_assert_eq(__gem_wait(fd, &wait), 0); + igt_assert(wait.timeout_ns > 0); + } else { + wait.timeout_ns = -1; + igt_assert_eq(__gem_wait(fd, &wait), 0); + igt_assert(wait.timeout_ns == -1); + } + + wait.timeout_ns = 0; + igt_assert_eq(__gem_wait(fd, &wait), 0); + igt_assert(wait.timeout_ns == 0); + + if ((flags & HANG) == 0) + timer_delete(timer); + } + + gem_close(fd, obj.handle); +} igt_main { - igt_fixture - drm_fd = drm_open_driver(DRIVER_INTEL); + const struct intel_execution_engine *e; + int fd = -1; - igt_subtest("render_timeout") - render_timeout(drm_fd); + igt_skip_on_simulation(); + + igt_fixture { + fd = drm_open_driver_master(DRIVER_INTEL); + } igt_subtest("invalid-flags") - invalid_flags(drm_fd); + invalid_flags(fd); igt_subtest("invalid-buf") - invalid_buf(drm_fd); - - igt_fixture - close(drm_fd); + invalid_buf(fd); + + igt_subtest_group { + igt_fixture { + igt_fork_hang_detector(fd); + igt_fork_signal_helper(); + } + + igt_subtest("basic-busy-all") { + gem_quiescent_gpu(fd); + basic(fd, -1, BUSY); + } + igt_subtest("basic-wait-all") { + gem_quiescent_gpu(fd); + basic(fd, -1, 0); + } + + for (e = intel_execution_engines; e->name; e++) { + igt_subtest_group { + igt_subtest_f("busy-%s", e->name) { + gem_quiescent_gpu(fd); + basic(fd, e->exec_id | e->flags, BUSY); + } + igt_subtest_f("wait-%s", e->name) { + gem_quiescent_gpu(fd); + basic(fd, e->exec_id | e->flags, 0); + } + } + } + + igt_fixture { + igt_stop_signal_helper(); + igt_stop_hang_detector(); + } + } + + igt_subtest_group { + igt_hang_t hang; + + igt_fixture { + hang = igt_allow_hang(fd, 0, 0); + igt_fork_signal_helper(); + } + + igt_subtest("hang-busy-all") { + gem_quiescent_gpu(fd); + basic(fd, -1, BUSY | HANG); + } + igt_subtest("hang-wait-all") { + gem_quiescent_gpu(fd); + basic(fd, -1, HANG); + } + + for (e = intel_execution_engines; e->name; e++) { + igt_subtest_f("hang-busy-%s", e->name) { + gem_quiescent_gpu(fd); + basic(fd, e->exec_id | e->flags, HANG | BUSY); + } + igt_subtest_f("hang-wait-%s", e->name) { + gem_quiescent_gpu(fd); + basic(fd, e->exec_id | e->flags, HANG); + } + } + + igt_fixture { + igt_stop_signal_helper(); + igt_disallow_hang(fd, hang); + } + } + + igt_fixture { + close(fd); + } } -- 2.9.3 _______________________________________________ Intel-gfx mailing list Intel-gfx@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/intel-gfx